
| Current Path : /home/cgabriel/ |
Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64 |
| Current File : //home/cgabriel/main.001 |
-- GENERAL DEFINITIONS_BEGIN
--*****************************************************************************
-- if file_open ~= nil then for zaehler = 0,31,1 do write_flash_string(1+zaehler*2048, string.rep(' ',2040)) end end -- flash reset als allererste Aktion
-- Parkticket Firmware
-- vgabriel / cgabriel
-- Copyright 31.01.2021 IfT GmbH
GIT_COMMIT = '0.99e (9efdfc75001)'
--*****************************************************************************
--private
SERIALNUMBER = '9999999'
sec_key = 'bd3edc6c024218258903d52eff2ee5f5dd5b0e9f211e210a3b75a4b0f9096f02fbae67e2c105aeef6a6fbf02944c7cc86a629d39f8dd9bf2f14f9ef8c8d55bca'
pub_key = 'b2b5a4f181bb19c4e81798bf43e4c988c308e85bf37996e3198aa121f6aa5221de9b2ed4e02e9c87e17cd14ed3100f0813833b73190e0232657f698e31930bdc'
ak1 = 'de9b2ed4e02e9c87e17cd14ed3100f0813833b73190e0232657f698e31930bdc'
ak2 = 'fbae67e2c105aeef6a6fbf02944c7cc86a629d39f8dd9bf2f14f9ef8c8d55bca'
--*****************************************************************************
FIRMWARE_VER = 'V_16.7_edison'
PRUEF_VALIDITY = '20261231'
QRCODE_VALIDITY = '20261231'
POINTS_PER_EURO = 0.8 -- 1.0: 6,33 Prozent, 0.8: 5,01 Prozent, 0,65: 4,11 Prozent (190/3000*100*PPE)
LANGUAGES = { "DE","EN","FR","IT", "DE","ES","PT","NL", "FR","FL","TR","PL", "RO","DE","FR","IT", "CS","SK","HR" }
FLAGS = { "DE","EN","FR","IT", "AT","ES","PT","NL", "WA","FL","TR","PL", "RO","Cd","Cf","Ci", "CS","SK","HR" }
COINVALUE = { 0.00, 0.10, 0.20, 0.50, 1.00, 2.00, 5.00, }
CURRENT = 'EUR'
CURRENTSHORT = 'E'
HAS_KNOB = 0
PAPIERBREITE = 32
RESET_FLASH = 0
DISABLED = 0
SDCARD = 1
function notify (text) print("NOTIFY", text) end
sec_key3 = sec_key -- Asymmetrische Signierung des Flash
pub_key3 = pub_key -- mit EC 25519
-- wird ueberschrieben durch:
sec_key3 = string.sub(sec_key,65,96) -- RC4 Signierung des Flash
pub_key3 = string.sub(sec_key,65,96) -- das ist wesentlich schneller
sec_key1 = string.sub(sec_key,33,96) -- Salsa20 Signierung für Ticketslots
pub_key1 = string.sub(sec_key,33,96) -- Sasla ist aber nur symmetrisch, also kann man es nicht verwenden
SERIALNUMBERMOD4096 = SERIALNUMBER
SERIALNUMBERMOD4096HEX = string.format("%03x",SERIALNUMBERMOD4096)
SX1 = string.sub(SERIALNUMBERMOD4096HEX,1,1)
SX2 = string.sub(SERIALNUMBERMOD4096HEX,2,2)
SX3 = string.sub(SERIALNUMBERMOD4096HEX,3,3)
TEST_SCENARIO = 0 -- 33
--*******************************************************************************
DEFAULT_price_per_hour_1 = '1.50' -- Werkseinstellungen
DEFAULT_min_sum_1 = '0.50'
DEFAULT_max_park_hour_1 = '5'
DEFAULT_max_park_minute_1 = '0'
DEFAULT_tagesticket_1 = '0'
DEFAULT_pay_mode_1 = '2'
DEFAULT_price_per_hour_2 = '0.50'
DEFAULT_min_sum_2 = '0.00'
DEFAULT_max_park_hour_2 = '0'
DEFAULT_max_park_minute_2 = '00'
DEFAULT_tagesticket_2 = '1'
DEFAULT_pay_mode_2 = '2'
DEFAULT_tax_vat = '19.0'
DEFAULT_max_zeilen = '50'
DEFAULT_report_number = '0'
DEFAULT_location1 = '12park'
DEFAULT_location2 = '12park.de, +49 911/148781-45'
DEFAULT_location3 = 'www.12park.de'
DEFAULT_pin_randomseed = SERIALNUMBER
DEFAULT_pin_definitions = ''
DEFAULT_tarif_1_1 = ''
DEFAULT_tarif_2_1 = ''
DEFAULT_tarif_3_1 = ''
DEFAULT_tarif_4_1 = ''
DEFAULT_tarif_1_2 = ''
DEFAULT_tarif_2_2 = ''
DEFAULT_tarif_3_2 = ''
DEFAULT_tarif_4_2 = ''
DEFAULT_tarif_1_3 = ''
DEFAULT_tarif_2_3 = ''
DEFAULT_tarif_3_3 = ''
DEFAULT_tarif_4_3 = ''
DEFAULT_tarif_1_4 = ''
DEFAULT_tarif_2_4 = ''
DEFAULT_tarif_3_4 = ''
DEFAULT_tarif_4_4 = ''
DEFAULT_tarif_1_5 = ''
DEFAULT_tarif_2_5 = ''
DEFAULT_tarif_3_5 = ''
DEFAULT_tarif_4_5 = ''
DEFAULT_tarif_1_6 = ''
DEFAULT_tarif_2_6 = ''
DEFAULT_tarif_3_6 = ''
DEFAULT_tarif_4_6 = ''
DEFAULT_tarif_1_7 = ''
DEFAULT_tarif_2_7 = ''
DEFAULT_tarif_3_7 = ''
DEFAULT_tarif_4_7 = ''
DEFAULT_tarif_1_8 = ''
DEFAULT_tarif_2_8 = ''
DEFAULT_tarif_3_8 = ''
DEFAULT_tarif_4_8 = ''
DEFAULT_tarif_1_9 = ''
DEFAULT_tarif_2_9 = ''
DEFAULT_tarif_3_9 = ''
DEFAULT_tarif_4_9 = ''
DEFAULT_tarif_1_10 = ''
DEFAULT_tarif_2_10 = ''
DEFAULT_tarif_3_10 = ''
DEFAULT_tarif_4_10 = ''
-- function SET_DEFAULT () -- lokale Einstellungen
-- DEFAULT_location1 = 'Parkplatz Albrechtstr Atrium'
-- DEFAULT_location2 = 'Bad Marienberg'
-- DEFAULT_location3 = '12park.de, +49 911/148781-45'
-- end
CHAR27 = 'ABCDE'..'FGHJK'..'LMNPQ'..'RSTWX'..'Y'..'345679'
CHAR02 = '01'
CHAR64 = 'abcdefghijklmnopqrstuvwxyz' .. 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' .. '0123456789' .. '-+'
CHAR32 = 'ABCDE' .. 'FGHJK' .. 'LMNOP' .. 'QRSTU' .. 'VWXYZ' .. '13456' .. '79'
MASK62FORWARD = {}
MASK62FORWARD["0"] = "0"
MASK62FORWARD["-"] = "1"
MASK62FORWARD["+"] = "2"
MASK62REVERSE = {}
MASK62REVERSE["0"] = "0"
MASK62REVERSE["1"] = "-"
MASK62REVERSE["2"] = "+"
MAXTRANSMIT = 212
if TEST_SCENARIO == 33 then
DEFAULT_max_park_hour_1 = '200'
DEFAULT_max_zeilen = '-350'
end
--*****************************************************************************
-- GENERAL_DEFINITIONS_END
--*****************************************************************************
-- LIBRARIES_BEGIN
--*****************************************************************************
DIACRITIC_REPLACE_DE = {}
DIACRITIC_REPLACE = {}
DIACRITIC_REPLACE_DE["ä"] = "ae"
DIACRITIC_REPLACE_DE["ö"] = "oe"
DIACRITIC_REPLACE_DE["ü"] = "ue"
DIACRITIC_REPLACE_DE["Ä"] = "AE"
DIACRITIC_REPLACE_DE["Ö"] = "OE"
DIACRITIC_REPLACE_DE["Ü"] = "UE"
DIACRITIC_REPLACE["ä"] = "a"
DIACRITIC_REPLACE["ö"] = "o"
DIACRITIC_REPLACE["ü"] = "u"
DIACRITIC_REPLACE["Ä"] = "A"
DIACRITIC_REPLACE["Ö"] = "O"
DIACRITIC_REPLACE["Ü"] = "U"
DIACRITIC_REPLACE["ß"] = "ss"
DIACRITIC_REPLACE["¿"] = "?"
DIACRITIC_REPLACE["á"] = "a"
DIACRITIC_REPLACE["Á"] = "A"
DIACRITIC_REPLACE["à"] = "a"
DIACRITIC_REPLACE["ă"] = "a"
DIACRITIC_REPLACE["â"] = "a"
DIACRITIC_REPLACE["ã"] = "a"
DIACRITIC_REPLACE["ą"] = "a"
DIACRITIC_REPLACE["ć"] = "c"
DIACRITIC_REPLACE["č"] = "c"
DIACRITIC_REPLACE["Č"] = "C"
DIACRITIC_REPLACE["ç"] = "c"
DIACRITIC_REPLACE["đ"] = "d"
DIACRITIC_REPLACE["é"] = "e"
DIACRITIC_REPLACE["è"] = "e"
DIACRITIC_REPLACE["ê"] = "e"
DIACRITIC_REPLACE["Ê"] = "E"
DIACRITIC_REPLACE["ě"] = "e"
DIACRITIC_REPLACE["ę"] = "e"
DIACRITIC_REPLACE["ğ"] = "g"
DIACRITIC_REPLACE["í"] = "i"
DIACRITIC_REPLACE["ì"] = "i"
DIACRITIC_REPLACE["î"] = "i"
DIACRITIC_REPLACE["Î"] = "I"
DIACRITIC_REPLACE["İ"] = "I"
DIACRITIC_REPLACE["ı"] = "i"
DIACRITIC_REPLACE["ľ"] = "l"
DIACRITIC_REPLACE["ł"] = "l"
DIACRITIC_REPLACE["ń"] = "n"
DIACRITIC_REPLACE["ň"] = "n"
DIACRITIC_REPLACE["ñ"] = "n"
DIACRITIC_REPLACE["º"] = "o"
DIACRITIC_REPLACE["ó"] = "o"
DIACRITIC_REPLACE["ô"] = "o"
DIACRITIC_REPLACE["õ"] = "o"
DIACRITIC_REPLACE["ř"] = "r"
DIACRITIC_REPLACE["Ř"] = "R"
DIACRITIC_REPLACE["ś"] = "s"
DIACRITIC_REPLACE["š"] = "s"
DIACRITIC_REPLACE["Š"] = "S"
DIACRITIC_REPLACE["ş"] = "s"
DIACRITIC_REPLACE["ș"] = "s"
DIACRITIC_REPLACE["ť"] = "t"
DIACRITIC_REPLACE["ț"] = "t"
DIACRITIC_REPLACE["ú"] = "u"
DIACRITIC_REPLACE["û"] = "u"
DIACRITIC_REPLACE["ů"] = "u"
DIACRITIC_REPLACE["ý"] = "y"
DIACRITIC_REPLACE["ź"] = "z"
DIACRITIC_REPLACE["ž"] = "z"
DIACRITIC_REPLACE["ż"] = "z"
--*****************************************************************************
--private
VOUCHER_M = 10 + tonumber("0x"..string.sub(sec_key,7,7)..string.sub(ak1,3,3)..string.sub(pub_key,13,13)..string.sub(ak2,12,12)) % 87
--*****************************************************************************
--private
VOUCHER_SIGNA_TABLE = string.gsub( [[
DGEE93TY3PN0SN39M7DNAR5AMS93ZKL1L6MPFNRQQNX4P865S0LYG76W9DMS236RFJNS4NJGN3EP6GE1B69P2DTZJPM3BJC883H035BBKH7YCZ9GPFF9RHKK
ZG9NE5MBSFPW1656P4B3LERDY08Y3ZSYC3RKS89RW12XBY57JJ0F20NF6M0FXL0PJG4PXANH727FXJ8C8WPTJQZYLB0YJ7NQQH89P8SA19506CNK8Q6ZMQHB
6CCR03PX1MZRD86LRH17T11NNBG56DK942WDMTPQPGPC2SK2QALRBMN7SGJ1YP2K9Z2RKWPJK8934BMG23G5AG4YB8TC50WN3HNWAQBYLLCX46A7X84L255M
DQAAXDSQTKT34DCXSHJQNNSXMPRYL5QSEJYRW6P01YPKL3C01WXY9EFRCF1KTNN84YQ7BMQCQ82CAQNB1X32NQ2C2F20F22CXT08PX6MTL63C60ELNNL8WS1
JCHC86H9CCRBP7S1H1DTBYMJR5XQRDRKJ42ZR7SCGAZF9MGAP1B79SYWZ01ZYTMSS1MS3Z2N86FY476H3XR5DYN1ZYJH9G83HEWYTHYRPGYHQ7L6MLH65H32
4966JKX5X48ZLPZEFHWWRPEMDWJ6ATENXZA35LS1XH4RZLL7WPEW8AYBCK2F7PX5GLSW6YXLJ35LJBHB7W5KCM94ZG3P0QL7C0E0ZRTLCP696NLW973MKL05
9NEJ86178M8XMNR6P7CS6KCDTZ76W8HG8WEEDMHBCFM6F89XKMDYZ4EJLHRNF40W71PZQGNB7528CZT22MXA0RR1HR13JH51JL4HTHRCXX7P6C9LAHG54GQ5
ZRS5HG8M90FXC0PPSA8TFY8LJXBMA63Q6WDNS75BQT87H56QHKRBSQJN9T5CCS7FCGFXPTMM2EK67YKNSSBY4TRA2MX8ZMW1W409SWNCQJ0SSLSR091CF1FJ
PSZ0ZSHAQ33J5BWETJ0BXL3APDX2LS3DRRYZS48JTAQP21CGANPLL7GEZAP91B81BP4WHGBK8A41B94WTHNQR03ZMBSQF4N8Z4YEDCFBXCD859YPAYMAGA8W
WHT4L10WSZQF128087WDEX30FA571CEFTM3QPN1JZRD427DT2Q5NWSFJY1Y9L548C1LB18TJ85MTJ723SRB3A8T7NWDENW33FKGGHJEF0BHDX7HE7R4WQAE8
CS333ZZQM3XX9RS4389QKA3T61FKBRHGS1R24ZTFYZKWHN6Y8DPHF7KMCEGZED30LCKX17STPR0GR8H87GF8PED3S1R0LC77HTMKCZ63H8RHEHFHDAF30NZ5
YZTM1CKJLGPSCAW14WJW6DA005N7LG4M3SCDX22YSWAXCD0TQE0KSMEL3DHKY5GDGYK6XC7EMWNA803L1PF6LYHKELP57F6P01SAL90B002W78N35XFBF3TY
JY81GKYBAA2ZCKRNL2W4191QHZ63YCDKD761T7DBWQRM4RM22R70WCX61LFTKN7X7WGCLYZPPZLZZ366C288Q9JRPCTJCD3G8RR31HBREJH9TNCLW73TD9T3
MXTAPLM558S80W8Y702CF8KJSW6X03Q98Y3M5A3BJ2SDZ4LPDN3R89KHGYB7D6D77HJ4P91HZ79DYPCEPZE5SXX5084RJWBY6TKSF6Q7W1Z2RYNP4ELPRRE0
Q20ZZQ4QRQJ0KZ8B4FMJ0MHD7W372J8T32KR9QPW5HJ0EZP275BQ3YBFXG8JEE64NYJTNHFLLCT6YYEB84AAK2LCSC9FJCC4E9ADSLAGKBQAC3W6TDT7PQ0J
7JD1W4SPAY31M24GMGMARYT9LXDDWYWEKQM3GH7840NFSH7ZDKQDSY8GF7Y4BX2991LKJHZ83ZWWEESKFJXEKT1DC9WGZHKKKFNQBG5GCNKJERQRJ9LQX83P
RM0ZXEMDR5B3WKNMJKG2SX2NZRQ786A8JMFC794BN10WA0YNEZ2F64FP4W60D92WNKM6B027ZSL8L5M62Y5EJTD8FG2RJHW48YRSZX9CEP6AEJTNKPDPYK50
LR0F4K3GAC58PN7DQWFC7H3TZ4Y4Q8DDQYNGS3B1HKNGK614DG9ZD5J1FSKWHX8GFM3EAGMQLMX040CWB5SHBNK8KN182N2JMMXHW10NPA0ZMNAK518KYZHA
GJJSJBN0S23C0PCXKN7GP95LXCF013549FQ6QQM4TS9HJ5BPLXFEHTA75588KD07NXWQ9YGFAK3R00PTSXMQZ4AWX41DWHG9BSYEAHYT8CTWN10SG9J36NML
HQAA08CQHECPWE1YJWXJFSP734FXHTYLY7ZZK1F85NMRDQ12TGBZM7PSRXE4Z52H4ADQ7RLJRBKEAAPBM7RC2NXX2RBAM5PFH32RCNJCBQJWK45GRMYX50JY
0T79PA4GX7GQ6LBXCTH2SXSA16EQMXN9G6009CBH0PG8EPTK1S36EWTME8L7TEDJZRESDH8YZ2QTWY6F6X5Z4P2WMREPES49AWBF6SGQDFRQG6W85RFFNK45
K2LXDXZF4RANAM80TQ24KHGG476H1R2A2REQX92B9E7CBSND4WWQ6SXNT5HW57DY7KP5BWKLQTKA7J0RBKPXRWX9RB05XRMMS5CWRWNQFJK8QEQ6FM5FYYHY
3C8QDEEF6JJ674S88NL5ECHYAXG7FCBP4SMWFBJM18JC0N7WPHRD1HLB7TX858MX4D43ZMAJDN9RPD2Q92327JMXA19HPR7ZDRY187S773B64MSA1WD3B6Q2
316B12RLX18T34DF1ZMK2ZRFSEPWZ8HRPP3RW3LHDCS4TXCW6A3L83144R6L8PLMQJM193K8ZN0K2NBESG64NB54B42AHR4GKF47P8KGMLLYLJ5GEHDEY62C
R4XH43DQQ9S8RRX85RP7KEQFWBR2XX8XB97YLZPDKKBSSMHG8GQHT5B0K3SC84QSAA6Z8ATHRD8S7FMK7EMD7SR1WSL1QD6KF9MWCPAC3JX0D1EGMBYK9160
7ZZS1PRZTMSLL1SKSDEX5A6CWB20CE709N52QM6YCTMSWZNCTEPP0S3JWME87Z2DZRP0MSG67W1LAYSBLJ6TNX691GRCC24YXHGT833S5DGZL5R7F7CF5FHQ
74707YGMJ3PZG7MWZ2A9RAD09JANDPQGZA7N7PMY7LHP4FTLJ3QKX3LH9ZZM00W35EB4CSJG0MFDTZS6YJ1FTMBX2R1K0X9TRLAD0RY7M0ZN3MAC07N4K7B3
7DWQN0YRT6D46EYJYH4DXN4A7HPTR7EBGQAGX6GZWYEDL52M2B64BRPF4QZ4S91MY7W02KBZA4TGXG2JPEM6L5M2E2BFGCTHG0CAEZQGER3RGMK0BZ84WDBQ
QF2FN0BG6GMZ3ECJLZKT2P4M000KRQ4P2722QMSS2H8896ZQQMY9LKNTE07EXYC4BQB6E9TNSNM3627SY3BM9BN8P7BWBZ6002MYZNY8P8AAT9YWLCRQGRL0
7JJX5RATX4W6H2JZTR829QZPBHMN43FR9CFNC51PXFA7KRP6RMEWL9T4ANHPBBNLGFEY1MRGTAQ6A8W6DPDA1MPB5G7KP600MYGPQZFR827JTEYMCLHLBGJK
3YNB7FQK0F4X7CXS8MPRBNMZEX94A6827M5XTED4RWBBM89EE1EH17DM58FHK97TRJZ9216XXXM1TR58T7FW2RRY8FJZ4YJ4KL6389QTZL2KLYZ8B71M718S
ZGMLZG2M9EARQJTFX0DYGTQNCN5G1D5JYCX4R40W25W900HA796PXSQPSEF6KTRA5XN53FR8TWRHJ4PYAANL1QD63RW9T23PJBL9QLRRPY44KHKYQK5R4A61
6EXCPQBHA4SK2AKW7Q6101TX6PXNK5Y16FEAWHQDL4XRNDW6BEBPME24CJH2FJFMCYH84GBXEMFD64GX2NWYDHKJ5MBT0QNRPZQHCABJH8WNGK2SJH62EYB7
80C2X32WYTAD3W211ZYHMX8G7JAHS2N4KC7CZ584PJW7BZ3CDC17PNP64KNP3PZ9BNMRLDJEWBH5NYSDE6GDAFGNE0P4X068FJQ12ZMHRX2NFCDY01LMZT5T
HEAB1TSZ6JJE5095SG5LYAMG7BWPDR1TL7EMD0X0D3YAPWZKSWFAD9MGEY0KYZ75NZCCBQNH6RQA20RCK0F24TAWWM32XFY2BD80B83X87ET8E5H117K2W3Z
RG6F77JFZZANXX295PFDD1TL9D0Z8PCQ00WQQBWFRDXFPJ4L43SWDBP8ZFXFXPWAFZJWRB4RZB0GTCTSZB3T0BNBQX8921CJ4974LRJW3DKL47GLFN8AB826
9TDGW4L76HYZ4KSTXR83B2K82Z4MF74HC9C86SK98SKGRR4FYGP3A4AELN7Y7R40W92DB3MA2Z8XHGS4BJT28BP3BACX69AEFDKQ4ZK7DZMT3HAG5BSC0KHQ
R197X3ZS45452QJQ0GYPC70AX5FA0BKGD7QP0FJDT8A0LRX1G0YR8Z69W53KJ9L2ED91N6LTKZ40ZBH3Q4C5LFPNT7J6EFEAN87NK41NESFTWBL11SQMLM58
KFKFAXLS9HKBL67A1M3XKKGANX4LTXC6SW5YH76SRY95BEHD71NTA03AA1GBGYXM2187CJ9CS0A9YQ8BNMEA941CS4Z5J7C336FZY41ACBCD66HA49T6620T
BBP1FBEQ2Y0FJHL9RSM6TF9A4F9NJ4111RWGBQFMP6T4PQANJ4AA3CT1PX1H3WKEWBT56ZCSQLQ8DKMW84D6T5311DKE8PS16BQ9N73Q475913GN1YA282FN
PDDM3FQF8MH8J1583BRE0ZDHH070SMFJ7RG0FF6WTNY6YMJDKKKL5X22SLSY3LS0L5711WX290EW928B3HJK73C9JXW9R34LSR872TLGHASAE42JL3P4GBAL
8DQ84ZCQH6A6CG8T84BB5M1BSRYM37BAGAJD8930C2M4AES4WWYRR9BA93H5FRH765ZKPYE3F71LH7X6EF6L42Z68DHWEELZ795A26XJ2GF6QK9CDCSWQMD5
7TRH8LDKPAY99F7G7M8AGYF1DEX3BPSX1S7PGZJ08KJREYZZQXQXFWGRQACAS2L6D4A1ENZ3J5C2MY6EFKRABGA68F9HJQN2GSF1KBEPJJW0KD1EXTG84MK0
97C6S3L5WYQ3337T9E47SSHD7CBR6TGCCR0XC1189RAL39Q5QZ44M5WJSLKTQ5LBMTMAWP40PNT7TRJRHMJXC7CX42GZRTN82MJHLM3D5LZP5LN23JJH197S
ZQMZRMFXDC7SA0FH1BZ8JZTTLAKM31ZRRXHR1BFLGMG8W70FX39CRXL5L03JHYKHF84GSC9MAKED11NGD9RDH5AW9XBAFTPSNZMSYRH7K9Z62GQPJPCMLFRQ
KBKEE4FDL9FM93JB552Y99N713JMQKL4T0WMCJ7K68GDALZZ76HECHHLYM3NGKHJE60JRQP7L0GZ1CCCFTKNKMEAC4AY8PKBSLTSCYTSGJM49694C7H2JWDW
60XKXC9DSFFXGHDB571NW2HMLLYFPL4A1BYGZ21MJZ1ED5464K663PJKESDC0WFHL0DWKLYQ9938PTKH6YPFR4DEQJKX7D74RG7K9B40ETWD2BTEC1YCKEK4
2PB53XFC6M2DK7RFC2RQMTCA2DF3KD1C3DSZNEY67X0TMGH6SHKZSW8C89ZH8J1627QWZNDNXRT1GSBM8T2H0T1WFJA9QBDTXB06BR6MYCQH26X1C4KNA6W5
YDNNZCT6GHWPHE54JELBGYDYKXRRL25MQL5FSQ550RLCEB92HPMDN6EJLLJ7N0J8TFW72RJKRFWE8GN0F5RXHE32A95A4F4AA69D67X7M4ZEDGYNWY2658EZ
R6Q4MGNHQTTSDZT7EKZK5HJNJ3AFA0BA8AND8P9L1FGL744Z8E5CW037PX8SSSY41YWPN0E1A18WK0SH1ZTXBLM7RDTAXWPG29F24Y0NTW97K3RARTE3870Q
ZHJ764QEA2NNXFPGJTPPEPKLYFJP8DMX9G5DDDS0PKFWEXKXG4W1ZB84QGHL1FYX68PGBJB6P8DD9XPJBTARE4J3E5KMTA1C4YYYS37J6Z2C69WXXCFR9C38
AWE3C5Z98HWHYLMBWYGD2KN42XJGPTXS0NWR5S9D8GTP9N70YQYX4R3BXTNWXX3SXMAYZ3EFR8X985JQEK92KL5DQ5Q9Y7BFJ2GLMT1MR0R89K58PP1JEQ23
ETLLCJ19B4PSAYHAG4JJ8FZMXJGNHS397KQGPRK74JTNKSS5QN05XB3J1NJKKT7T7AJAAP9NHFPMEK3CJPSC7WWC97WBDEEG028M9RMS0LHY01DEYFJB4BZ6
FS2N0PG7WXJ0F05ATX7H0ZWZ7BNRFZ7S8LSWEMEQ1XXGSYMBFJY6L2Q45Q4TFY7959G7N1AYGEZ55XJTCKCSC7QSAZ75HXPXZ05GHHHCWESNNNT3KLWW4TCP
JGE5AD1QR8FH0BTJHBJH7A1TGF0EZRCLGKB82C5X71B8CDZTZLKNGAT3LH4W49PD77B5GJYQSC5XJWGLP5L0Z8NW0HGCA0JW81N2AY9YDP0W97NT4LHLCZNR
EGC07WJSFJDLK8MXM94NE4122KNQ5NQ70BQQLH415WMGYCA8MN87HPL7JLS0EJ811LN824PR65GBA8SHMHNXZEM56ZTG5NH8LZLNEERZ7GN9XSS1QSG00WTM
S2WG26XNPYNDLN1HG1AA6Y7K6QLM18C718P0K2F4FFWYXXJA0N18YCHBBWKDY2Z9TG47ACYLP2M240DD05BZARNWWKQXCFZL6LHTKJ8JBDBYYHLZ2JCEJCPB
WM8MTF3CY15ASCN9J72SNRM2JANQFQ64EJ08CN7EY406T0C6QQ1QQP9HKT0EYM2LGKCYJ0LSCT15HE40CKX74ZRQJ0CKW80KZRS50GGR4HM5WF98DZKWCSTW
LL3N3NKYNRM0G2GE7W6L4C3W1WZL3JNKFBJQBQNBQMPFYFR346JM09RZ0ZWN59JTL8R45RYEMNRQFR637RAQ960JQJ4E6MMZE8L7QMHE5H3T4FLYFGP48X85
307FWJBPZ46FFPYTZC2EPR5XGE32RARCM7PY17RJR6C0SD4MJBB4BJBPXGWX82QE5858K5GS4Y5PLF5X8JBAQ0SWB3HSMGF3XT6DQQXGN10EGTN2ABEKPFB6
QY1LHZKBJYZHN7DP3RCNTC23GN9FXLM9MQQF048DJXT455HSM1SPED7TCEHQ6X149R344AWML00B939NE3QSHABTGRR8XTGHTR5S6NHZ0CN4H9H95G3X80G2
GZTAJ4P64ETNEKZ12GK6K8DGX4ATDMGY24735AXFGGF1579RPB74AZDEE08K92AZB9YKCFDR7NTJ6FKS8TP4JC2XN1D90FSQHAH159T4T28CJZXZJWLQ8N98
1MYZTAFS1378LCGQ827Z65CRRKPQK1LAP581ZW6JMWGB83ALMW94NMJRGHQLKCZ4AFK8J1Z0F3P9GSQ231B3Q13FK61N04453QD9N14FEHH18G064QXGRQ6S
S3KL18BE5Z85JG12DK07562XKJAKH9N8LK38HG3236RT81G7A7XZWJ738W9F09WT7MRNW32MX2P9F0E58R21JLDZP8BFRLHBBHBLYMB0Y74GQWWLWZMB97NT
K7ZS4DWY95M23SKC9QYY125FAC8C08LESW0QQJPYF5XQGDBJB3KDMGNHR8TGLQ101H2R5W1TRGRGYBL3G04W746703QT6RH3H73XLEGL1Z9TRPLGX2K5B1JQ
DHYG44N5Y3YM4H3SDLBDE4YNBAL0BF2ZBFCY3CMDHQ650FGR1PE5HLLYZ7GNMAZSLY6KEJX9Z5P31FC9XJP7FE37ZN6XSPDGRGLEEY89M5LQTKYGPRQ2E6LT
6HZTZ14Z4XTWFN0TLTY871XAT9DMDAN0PG4045B51DNZD0KA4SJR736CLGAAMSM5MFTFTE12S1AXHRELA2L9MH53CHX6Q1HEGSRJ0TBLWWR62LZDNW8MADAX
TE5Y52LWD57RPF59J5CBBYHB2FKENRRTE597YPEELHLEB781J6DTBTD5Z27ZX7JN477J6ZXHEY9HBRKT722X0Q1393RZFKZ3TP93GZY14WK1WPWE9HRJG19K
D95RB2CDW5HK2JA77Y3LNM69F676BDYH88HS11XXQBDT10DCGHK0FYJS7Y1FE9BBRYWTT4ZRY199HM5CFGRSZJRNSTX0X8QFQTHYSD1652D216QG8Q5FHMX8
NKW2HTKGXMZD7JG4BYBDBAYSK8EMDRECSPQ814X0F7YMYH0CJQ41JK7QZZETHA58SDL6WR847P03G6ZLKLMQFJM75BC2WAT30QFJQ9MATW24D6XXZ19BARXH
BP7LKCKE7E2AQ1TRXGJN832FEZ8GZ9CN10L6R0ZBY62N30N87J8ST1E8J32LLQL8X7JFQ58NHTPYHPPRH5ASQ0HHN72EANBE7TYSPQKGYJHJHZJE2NFT03SF
0CR5C370LB5D6G17EKY95SQRS29LMB1WGTNRE7G6FW05KHJFMS43RGCSS5GHJHALKA5FWWM4GWF5AC1HXK1F5EEZ6TDYS0MRM30YM9JW0DN5J1X3A0QHYCDB
LK3GLAGBX7C1D9FYMXM0GTN8H8FJ6N1HKECF0RQAB77HDM47TFQ4H8GEL62Z60EYYJ0ZK2QTLSKAH8WKWTQANXGL5S9H58J8ZEF5PWQE34GY0KAK6KR6DGHA
MSP34H6L957TNA8BP2CFCFD0EE8KTFYNLRBXHXRAYB3RW639EHHKB9LH838NPQNDQ7NG5N88768L9AQQQ0H6JWNGMMGLL0ADQA1ESNB9AAPC1M7RWR6726PY
J0MX64NSP1FKYQB7J60AJQABQ9FTD2WWKY41JH64KMQSLA9X9JL7ZZXFSKZXCAH265FYXMKCDHXSPZETLAC98P4HJLB9GYZ8MBP1S4EXLREDHSMW1AGPKDZP
AR1MYJE81CN6TE5QE6391SG913TA4KKPK6LSYA4TFHCH9S0Q1NYWKH0AYHTJ87HZ228XEGZYCY4M6CLBCMLWXTD0S5DH49EBZBM9P2W3F6A50GKNW0944MCQ
CJ097YENCBTBMFGT5MP92JTHEFLW9S1MNHN6JD6WZ352P9GLR0Q2EY6S0H3J8E0MWFJBRYLHC77BEPJEXZPNHSSC8W9LP439CTEC9ATMWCSQBP38G78C9H4Y
6A5SNK3C74HENSB9291M1DDHZ7TSNS6614T18EESWW093Q08YZD9TJAK13N126B0NDZ8S9H30C5N79M3S6L7YZTTRT2X35SB6MS1NQE076FXDT8X3CMCYP51
1R0DMLH438S35FPJM236GA5NF3C0BAHC6C44K5MWCZ5A7LNJNBYG2MGJSREW69M9506GT4S5EGZHTG5TN84WDDBFEH4ZA0KH7YDCKXAX6873XJL2ZXH1PRZD
4L1N16ZCBTHTGPZ85N61JH0L7C8ADF09YZT9QK566X3QWY5RRH1KLHFH1WQL4XPJ616SA419BE1L585M08G106JZDH997T7WWWCD7L8X6PYH5MXTEAWY9BH8
RARB3755LS6HGJB8H302AAG0TSZX9ETW09LNE7L69ZPJL7ZW03H52C9G2SAZF3S6L0A6QG91CTSSC8CR61MEEN1NKBGEG5G99MC12598P3HBAT3TWS8PSNQR
BFEP111XQL0TB4R614XBS8EC4BBQB6QF53XQDGW3TJLE8LZN26BDKH48RYL3BCQJHWH9KAL7R6Z8ZC860SGL8DCQLK642SM73XDWLSEL19CJM6YW1ZPNTB88
9K0ZR9PCH3M22BN0L3EBX55GWL0FJ29634PC6QHN7XWL8W135MG1394MQDTZQFP7C8005C04QK25B0KKWLA5PN3MCLXBYWXE1AYQXGGW5ANJMKQR0K5XJKT9
FGEGSRT769S5KMN0HNEXK3FE91R76TDMJSWNAF7T49QQNQXHQNBHB89LN9HAF9HD36C2M0G0JXX4WJ51JKJTZ78GE08RJ0ET8J0EEBYX8F3YDKTWP6FGWW3P
JAN85DRD2GKNPS25LMMAGNC0AN7D9FC753SBHSH65MALHD7W7KCQJ4XRH5EATJWQT3AS2PTYLB5WH4ZN8YQZK9H8N9J177NCFAXKKGSFNGJHELHBM0RGJHXJ
A3S9FKMAY1RSHALHMXEBY9F8J0XQ3JZ2TMGHZ2Q4FPP3SDYZNLNRQFQH690X7BJCSLTMNY0XNX8KNPMPDMN39QYCYGPBNHZK2JZWKB4KMGN5FMLRDE38CD6D
1AHYPEA9X8GCSE2Z122ZYNZLEY3JLFWML6QEGJQ6GXF5NK213H4DZLSTMWAB4DSXG4N3M8X2TSYTNX23B7HR4Q9F1CQKJ4DE5CJC638C2Q52WRM5BF51WPGX
BXD1BJMKW0WENYJWN0ZWGXNPKLY3EZQ473KF9ZE0BQY7NSLNL99THT18QTA3KJTT96HFP966172Y9NRP9W1SF9H2FQ8EHNMAAFLKHZ1JNFSTFSYH3TQPF09A
XR0BNWWA4NY5G90GHLJ3GKYR7NP8FT4S9D9980CG0D9BM4PF2HPA8RASNSHXB5DEA8Q427DKZ8TX0YSSJJYZJ7M49F3E14A70JYS1LN43T3NWWS776P1WLS0
Y21977DQ1L4W647FSFX3JE0YGX9KYBLT58XJKNZ6LLDZ2ZBC78HYB3TP8FJP8F1D9G2B0A2TZPKA0TFQN3NBG16XZY2X0FNJ6W18D0NTFJ366QKA7K2Q6LWQ
5Z99796WG31YPY9XSLL1D0BGE5FG74XJEH6HSWAM5B8L3YR2M5F7TQPMD22ZB4LHLZEGSGWZP1WB9W5NRT7TRANEEWTH68EHFADL88W5NGKTKGTSJC7JF6XR
EGHGXWDSDJ07GJJ0EQ4FFSEC2HWJNHKDDMYNYLKEXLH6F58YE00RL1CNK8JJ0QWRLXRMRS7X0NLA5366P86EN0EJ22QK91SP0SL0R4AG4G8XRJLNZHDZCS58
DDZML851H7H4YQEJFBC0TKFJ8R56SBKFZ59WCPXT6BA7A015PETYFHS94GDPN22GYGKEFDMMZ4JQDSE30MDAFX33647A7LMBTZHH1FXJCCH4X9TSR5YFYDP7
9A9DZRHKZEMJYTQDLG9RJFSK16E9GCDJF827Q610ZPDHY3T5NFMJSZQ6PCCF4JD3ZQFFSZAM00FGNZWPBRH9HGDGY47J8GTFFHSEE1BYH8HZF6G86DYP1E0T
28A123GJ1MXMP8QZE0CYMYPJENH5DCPQ6SRL2ZFF2S9WHYS9GX22F93Y91MME5LGG59SW4LHQQA0PYJ00BSALW7PXJLN07G74K968QP5MST566TMXB4RLRPY
KEYEHHHLMSHEJZ60MCT4FX0KMCS3DKDXFENBWHJY85LG2HBYTDKZNEMMKHSXMFJTYJE25XXDYYYFJC0AJLQ46EFY9LFRYD91W8FRTSDWAL4RKDAQSJZ1X79H
H4HJWSSG4LZS01CM89MHM4GH83KTF7K3Y8T2HBAL48GCNR6M94XRMA4DX8MSSGW6X0E7P04Z2D1WEBYQ10QQMGB1XK6D5E0Y113RH1H6LJE615KWTH1PDCYN
2BDY4YTKTESS581BZMXJ3C7ET3D5HL574AB56DGCSM2ZEFRT9XALQW6RMGMXT3MAXSHLXCKE6CTGN94TZN80WKGRL2FQ8TJT75QMA47TTD41MMWXM1ARAMRZ
YFFQZDRNAQ5TGJ2J2BS1KC0GE5ZGCZCDBP3R33NX75GPFAHKW8A36MRZCZCHDH9ZGLZRJZF1W6RJ9HQ00KN8GRXYZQJTG6J3Z535WPQZSQL3GDN72PKAPD7G
3Y4N78BNCG58FDXLBYJNE46NH1AGL93JWTJLN1LC477CGMKN8PP3HSBE72THGLMS80G8HT6LD2KYYNYQPN7GSXTTJ2SLRZG83BEYEZMKAZA7G6S06T7TZC25
QD7Q1Q5E66LGQ35KRH1ZN7BADXBTPMQ86AL3JB4R2XZ982B86CXAGMT1TEW17TP2BEM1H0T7SKD43H0XTAL448KAB37FEAJ4GGSCB7GWT16EJ8RAJLNMCKHZ
9R7Q61G8457YJQXARL0JCGR99NR5J4274LAR8M8G3SZ9J63JHPQ10QX48XXZB4N1XS19ZNJYK5WP4MHL508TXTSTZEKR2K1JNW6Z9Y4BM76FJN1F0NZST8BJ
B9K576F7DMN88HADR9RJTTD41TPWN6B7DTJG3DP46Q0KGM2GNXLTGSSH7J67Y2XE03TNHYHNNCJGA33S8JBXN9B77223H4JCT63SBYK7EPQ076LSYJDR7ZB2
A58PFZ3ZQGMAHG9901483XHD39DFSHKY2HRJD3LBR3WHRGA04NW1X6LQCA22QCF9T065NHKN1BH1NPZHQAJWTF1550YDNG81BKQ7MH7PYNRRPH48Q8YY998N
4PC4F6SQY4SQ5T88T8Q1FGP22NFZ99GA1YNZBKFCQLBEQSBYHAT38Y8TY9KWFLDZMDDKXNW9LLQC0AECTMHRQFTNH6W3HQYXBEDMEQRACQFRKRD78T7LHDLL
GPJDMSPSFCZY7Z7G0MZL81HE2R66DHZ1G5L2GEC7JY314FHD9DSR58LRQH6BEPD1ES0T0NDEYH5JBX2EPCLE76LSPHWDG85T4TXD1PKTZ06RAELKK6F1WY69
4DMEB3MYE1EMYFPH6AHT0W6EEMMQQ4YGWQTL6PMGY72TM9SJ3QAN19EMN7E8Y5X2DZRG0M3ZKMKZNNL38G3NJJ5JY7ZT470WPGKFW9BBRTRR1L3PTZQ6LDYM
ZHPENFPQ6J2D75H7QH7N20QGBM8J7Q0PGFD0S3X1945DXXZWBP95F19QQYZAP3K0Q26485SRXZEK6TBX1LYGMWFL27GWF5J2RYBQMWQTSC7TLFG8YHXQAKDE
11PZ8H6QJ25GZE48JYRF0Z9ZDZ005RYQTN0NHMQMYDW4S70PBGXP3Z0HD6367DKCCECT77P3Z918DH5HC1GH4BEWXJDMWXA8RFRB8P0FTH95ENXKYAMK2BB4
1H0FB5WBTWFNT6XXPHYDZ02HM1MYDSKLQE1A7XWCHJCSRPDPGL3C181QNPNCKKSHX6QM51A2AFDA2KKAFWXZ1G01FFEEDF9NMY539H8CBBXMTDHZQBDNPRYX
F5NR1TRPW5BGXSR89XDXN978EZ8CZQQBE83MR3L2K4SDPA7TEGZDKNBXGSP3J8G2LNYLBMNTFSMXYJZLECAF3WKRCJ39GNGTPZQ628CKH6D4R1XLELYJNAJ3
N9L0994TSGCD2427NPYNR0LL15F1ZDDG8ABRCY92H5Q6F92X1XXTSRQ7MW3284ALN253FP8BLL2YP40YBS9WAYEHX47959Q5ZDK73ZXJWJSTML9SG03MG4FA
MM3CR7SZGNPRZ2JSBNBSELR113PHT2PPYBBYX9H69Z6SAYY2R3CLJCMSH5QSR8J1AT9EXSZH8Q8JNQBN7GE1YB7JKAPK446PSCDEX19TRYANN2P7AD37H0YM
5RZXJQWP7QD5N8PNQSNQCQSX32MYKQQ0QFAHWDGLB177P67M16LLSGYWS8XRL9GDHMT66JZ2SN5JAJGBYT4AJJDPGNNTT0MSWPTDHJP8JEGRX3BFE1L2SAWM
SWNBK59BEXHWCG19T4WG1L6MMH1HQXXJ4218YXS5K8NWXYGGJK3S0XH9BJS6CERTNTRRGA5LJLN3MGRGL3MT8XXA3ETZA9KSXKSARZZJWG9R39GJL2AC4LX1
6ZJTKQ7C49FJK783PY37WR1P2B3BJ8CYXW9DTE4SQCP4GM108E81LNYRBCBT7Z9MSF6BYR9PCTHYNJG82XPN2RNMD7PGN12KZLT45FHAHE6Z28X4T24GXH2P
R90RXSS70BY9843F2XBH985XTX6GBE03Q73WTWE4L5ACX2N44B56S3LBQHNA9FX1MQXNTNHQ5Q7YT7KAXGTENQ480HJJ33SA6DSCD87ALX7Q51J1HAMHJFFD
3CWGHCMN4H39FF31SFNGZ44ENF9JA4229HSS1HSKLW71J09P690P6CCAN5RSX2CZ6X8BBHT46DKYYBRBE06ZRFQN1QSGT2C3J601N4SA9N72B754QHEPB3QD
6J1CBEZ1Z3WG138X3K4GTXDRBSP0W8YEGTJN851XKXAAQJE7QWQR4BYY9NGG8H1XKA2ZZF75SWHJ79YSGPAD66LD69YJS9CRC8HYWN8C909DJWFETLSCZG4P
JK4NBTNGRHHWZ08S6RXR2KL000R37J71QR169189JFGLA20TXA94YQDG2ER527Q9BRCL16QSMFGHJBSC2BTX6M6E767L2AP5A3B7FA9TLKSFTYP0C1AFQTRA
CBZ8W0DDPZZXQKGCNMN18QH6RQ5PYMKDS57C9Z4TCEG7F3GXS0GHXCNFBL2333HWSRMBZFEYY13GDNAX08BA77LX4N8130XW01LBZACHBZCHZFB3WF2E8NN0
P8H7W7LJ371YWL4LW9WAHL2A1HGNH0FZW3NGFELPZ8NA34G5ASHPG6K4NLHZTBDF0C76KHAMFSHPQDLJXWH6D8NDCZ1LR51GSC8R14MZPX0MPTG2FDENBDDG
1591BXHWQFWRCRHBXCS2LMHY69Y2MEAQ6CR2QHZ53SMHSXYEQAPC4GDNKE2GAEFTJFGZ332Z2YDDFEKWGJ8DL7CFHQ7P38Y9WZS5YCWFDQNR3K2NP1CDC4RT
ZBYRSTW3K3SSZCLT7E9FFZX24PP9ZPXC60DTHQLM60HPNW16P2M2AT4NYZ94GZTQBMRREL8Z1F014ZBZ6S697N9BGFY3BW60R6GLH29KJJA852XHC5A0LCP9
PNL69HMWNWNZHT55AHCH6PL0EG6ARSCNSS638J5MBL2RQY5K4AMRKB0XN8LR4SRPR678RJEBFNBWRK3JRAKS2LE2C62T1X11L3N59DJHDT6E7X27E39PK89W
JZ0S6Z7HR72WZ3A1ET5ZZYNTCT12RXZJ7BN2N76R2EWA4C725J6WK60W3HZC7DWXZ94GDNZPESK2XTH1APXRAA2XQ6QS01DBXWNN9GQ8M411SK9YJH1NQF25
MTZ92TL4FE44TGGPE4YF82GG7HGS3SZSBD3M5J0TXCSZY50LXZ1964KWLLCKG1RNL35B9TDH5FDMTY3GHDKN9P9PEMJ1QK1CW8G0R1C5G4C4JHEM9X502PFP
LCYD2T4ZRE3R5ZMWE662PG1168HE5BDBZEHAML8M6A84QD63098FKPZY5D53HAGDGCFWSWYTDQSB31GNX2FCW7ERZ7CDWYJKDDBF8CZBBMF0P60WLA8N93NS
0CXJDCQK46DGFTP8NX2QLKT7RR6W3A3PF8HN7GJY82H9GZY06X2HCGNCGA8NTAPZJZGY3LTARKCG5D7HP5E39EZJ0FJ4LHEFET04Q4NQ83FCH8TZC8RMBHGP
BEZJWTCQXNZ9HGY0RGR42TE8REE3W17L5NXNTQQGQFJG7S7B5BYRDLQE634DDRS6RE6T3K47EMQHHAR2WYXQMW345T9693994CXT2BDPSBZ82ZTH4ZGFSR3Z
T5QXSYHPE71PFLQEJF3BBLYY9ZG3ZRRK5PDZRT0ECZDNWBD7Q4F620BZ3PRYPYSLNX9NZJEY5KRN9E6YW5BH9FE52EMW5ZPL7TLP6ARHRYZ95P2NL5HPT9E0
Q7JDN4A6AGATM6K87MR45X4B156WPGJ8QQ78Z9K8KYRG7K22ZXB2KY5A8HC5CWCBTGAJKYGP1G035S82DS426XEK7J0G7RNCZSW56SLBMTGC7X8ZE06ZAG5W
E9KHWKTSNFZ6EW2BFAGML2XASL8YTDX8MC0RW4AW3QDJ1P1ZS4207459XD2GJ4X09SGGQ8K7LX182WXC6JR28MZT66JRN3G923J96PEZWMFHRK17QTL5P9QS
CZACY85F6049X2S4YEL1X83WRKNPBQKZ0RYGQ87K2Y7JX9M083H96Y07S0AW2RSKY65PKJ43SYTE2NY1342FKSMR54WR4CSJFZ4D5M4GQA1TATW2AHKRQZYD
E2D28F3WB18JPGEMM5PHP97SATAJJ6WXLQJCW9MLXEFW1RYQMRGL9P0CY59S4GE2X284GDX6SKD6SESZE59QLJ750EGC11QG0LEBAN21KQBNCWGJRKHLPR7X
EQPMRMW03PQXQT04YP4DTE2TX7827S4HJHFJF4EPZCW05KDM497A85B9JAG1YEJK9CP49CZSHA5XYATJ3DRAA33R2R1FEADQX3GHH3QLXDRFFTDP5DSHH2H7
2KMPR7MX0WB3MAK1FKZA71SJ5R522X5HCCZD02P7H7FF9EZ4XCPMLJ07K953ZB11Z4LBBEXYZSGSF3QSH8Z2Z3L5LBS907XBRR03LSD9YY3GD7M9KXGM89C3
L251LYC51NSMSFR6G38YFE2ENA78CG6KAHAZJX2AXC4DN400T2ZHPAWPTLMLT4FB55N01W508FYMCG9LAK2SYZK99GQFD286Y8MT1CHPP4LBEPJ0435KR07W
7AJZEZQLXL80K0FJC1TKR6LJX7QFQAJA188MF5BS7S5TL16S9CRWFL7SHAEG12MWTDRZ6BM2W3GNEQK87K1M03ZC29R0ZMJJ7J5W1CHXZCX97CY22T1SEJ2N
62FQ1CH3W2MX8XKFQD4SJJDBALWGFZ05A6E70W0JZ9CFZ2A0QP5ZHARPZWF7SAJ192C4ZN8D96RSL6LCS71Q91MSF9TLCAM5E6F969MCD8XSJRXN7GQE4LF3
QHK7QZ4CQC5K5C2664TYJZR13QM1CTEBSXX4639SZPR2B2EXB8FTX0W82RNQ9EABNL16J21591RL0LRPC0YQ625EX5CZ6FR6Y19S02G40ZX6WFSMAAETE7MN
46ZRMZ684C4DQEW2THLFB09WMQF7S97PDPP60SKDX8J9B7ZER9Y3JW501TQ7FSANJS6ERYSFPN476EZ7QHFZ6T3B26XKTLKA0YGGT57YLRBHLRRC4FYNZDB6
081HCAPN70GC9QEQQ4GC4G4M7KMN2S6KHB8TWMTE94KFZX2FBAN184QK8LK10Y6C3KLMG87JGTD3GPPNKPYZZNMXEGG1ZQEYH68CJSWE7Q4QMZQG0PC3PA8D
8XLF960S9WX734Z0GLXWW9SKTXWYEYL307Q4W2PGH2Q170ZQT6T0CBLLMPMBRB00XXD443ELM3MKNX41LAHSWWGYTWCT5R41X9SB5FJCPGWQA6RZ9H806XZ3
41LX12G0G5P9T45YERPG7N3DZ6YPLH72J4JA7LMP7L1CXQENPC0ACNF160RZ3B69YYCB5A6JZ3W67Q6ABM1GMGH4B4LGYZJ1HWCXC031N536DX9ZT2CPATZW
S9KASP7QTG95BLQ776WZL0D8S4MYSXXYZD6832LLAQBWDAEZTPDQXJ5E83548YJFEYWJXPFE88PBJCNA3CKQW03YPKJMAQ9GP0E2FRN9QQEZBEQ0KH2B383B
Z9SQYBXNQRWWY9GP1TGCMXRZ7XN0T3PKLJHM17MRBY232ABFBPNS2RM5X5TGSYLGK2W65Y6J1LT0GD6PCAK8AGCLWLT3MQNZTWQQ2SZ00K7BXW7QD1L0HK16
02X9TXK2449BQSPXNAM2CKTJAQDPH5S3RE5TN0G4DKY3NN8NSE2XX0CE24C7XXFFN1HPHCH924LW439PJGWCBMKM02KA4ELH5WZX8DZ1WWF6MHS0D3RHAKFM
3DH1K2KGH56F6PGDT60PNLFDKF9YD484W0SBKNNM569HAK9B1471X85DWPEQH3GAN52K52ZBEKY0NWPY76T5JQ678ZSFYM5R3RSS2M9NZ2FGZXZNLKLXHHM0
DZ6EXHLQH0479A6KEMLT1W1MB81YFPGRGQLA06JC8MGR48SWDG0LKZ39FEWACS6R4ZADPNJ55EMFBH4HCC221MGZ7SQ0RK0J5M2L5B73HKPYMZ461BEWKTAT
7JHBQC1E6F6HQ2103RZAHETK4MB8N2N6BH26T0Z36Q1KT9LEW906Q4YG0CXF8X5SBYTDZJ4PZKJJBXX5HAHGP4BX98KRTGAEH3KETQCJ4WQY4W8LCF9BCL8M
3HMLC4AT1YM2P63ZD649D0PJ61T08N9AZRG148WSQYCM7Z2ZP7SCQK9K6FEJX8N78G3G25MT8ANSQ2M8LH6D8D2JNPQ2MQLEXK1JGQ62KACHD60ANM0M7KG6
JXLG971Z195H1H6AP0PCMXF5TACCYR0KX1LZC8DERAWTPEBQY77QM8EP4J3JKPPCRE1DBLK3Z3E044XKR54MCW59BK794A5JGQWPC371GZHEL7FL4339MK0K
N8B4JJMEHYC1CGWYCALK9Y06DPD6JR1J8CMZWCTAD5BJ9LK5Z0GSZ7T1653YMDKEJGT7R2WERLXW7DRPRGJHPAKAH96X8YNGZTPH0TWG8HKSTSJ95QSLKNG8
98TRPTZ0XGCS4MMGFZ5ZM0SCXRSCR56WDC235ANY8360AJBLLKW3E3HNC8CDD08JRT9MBT8ALK5GQJTDQ0HCNEC3344HDGMD4R61TK78P4XM8Z65BNA7EF04
3C5HRC9JCDSL2TCWAPRJ070Q75AZT5LEA7W60GRNYSLXBSHWJ4BEYQCBEXNM1E77HLWCTW39CKHGRLKKXTXFYPQLWQBXCFM1YREW2J08PC2PSCJDLCRMBQBL
MFYAA876QHDT5YREE2DFQ2ND8R6SP2GLKHGR0Q7XGZZ9HZQW634KM42D4LFHN7WKRKK041SD4885XLNLK40GMS100Z30P5EN0XFDGLKE3Z7QW7DBRRNMAY9M
4L9ZB8ZP9B3J492TSLY34EG4L00GGJWDFGT82J5ZTSLA3RBW535XJ93XJP6EHES31BA0THB28JDAKHDG16ASDXEWWZP6TKX2TLSXWW41BWBTAZ47QN93285H
FENHF26ZRT620RFQW8RCFJG24JMBZCR7KNND2612C34N33856QA5HMY508J37K01JFL3M88AA0AXPJYAH6HEJ46EGR4QKCNPNL7MCK4XEPKDHSFETBA6YTGH
9E0BK07J120ECCRK3K2YRK59TZCWKYA7L5FF9MD21YNEERX4SJKSQ03Y3HXWJELX7YSD8FPN0PDYPNKJJPNHF34PY3TK48DYQFL5SEL5CAM3AG9N09HALLKM
K6Z8JTPHZDX8KRC2A68TQR129KDP0XLELWBFF73BG8S582YJEQ63Q3M197Y45MWBR5QF468EJ47Z7B5HAH1KYP62A77HJERK6PERS3EN1ZZ3RW16691BTB53
F4YD212LRPLA76ACP8HP1JS3760CX9HK642ES4AAHZ1NQBCMS1L6AGX9QAM93X7F35RWX6D7P57MJ0DPHAAQML1LZQQRWA0MHGY78EXM6MCRWJNANAKMGAB3
KACBTNKK0AT20ZY82EQZ28QW4HREP3J4291G96LJT8B4XC0JK253TEMAW8GTAESE87TYW8K0L9CY5NSG2GR01MMXEWZGQ815KWMB1GQ8KBPG7JBLLBTMSPTF
9K6YZ385MDKP145AYEM3XKJR968BAR83KKD5973NG9Q46EP7A5FEGDEJG31M3NAWJ0N68B6XLSBZF7NLJX6ENZ20RFWCXCM3NMF8K590GL37HB45MN65GL9Z
10YGR8RNDF8XTRAJL2Y25JRC47SEH3TKPWLP1LP5Y519M76B5B5B1MGSKM08A4ZK69J3QHKNX691WDDRZABJ4NN8MFLH398F0GQ2DH2PNZR91DWJ05QD5873
9H15AN150NYRHXNZ7RS3D39YKYE0QHN1L18G1P9HQ2HGJH3YH2D9MMWZ8P35G389EEJ5D723L7NXN3FZMNSDM31DBE6N4JTWXARP927PL6WLFJ9TJB50GQYN
C7PT1Y0070FESMC0J81CGX5E8B1X6T2FCWHE2C15RG6S2F539QHKW2FYMG044SC93188ENCL0F0SATWCYAW2E4F7EAYW1DGE5XB72ZHSBCRW52MMEYYSKG9X
8W4PQDCEPZZ9CE3GJCS08Z9B42W8S09HK6ZH0Y9FHS41014EEQYB020CHK7QLE0FG337CLMTGF27Z7HTA4E571ARA9QGQX7X8F571CCHN8RGSHJY52MJ04J5
AHWJ7ECSRST1XLKX3SAWQNXD21WKKBGYRSS7029GDX4NR82C4D8GDT1P2FJC8LEQ00A7S3QX8GXQX4BBWSNQB1MMGG6XTG94B3R9GQHXF79SREW209TP2NLA
GHE4CQMG6MKQLMW1J06K1DCH85GT59B9S46XG46QFYM1QR9ZGDMHJALZHTJ2FTGES8RM07L6BB3A3LQNALHRZWZD0LQAQXG4TZF4PYN6CB9ZJ1ZRMER2QZPX
WML1TTSXKLB45BRA098EXFETRE0P9XWGTP421Q4AC1NACN1N9NDZFKJFFE2HRHDTZ9G4YDNELPGSQHK1HX4BDMQNBTHBS9EMF10G5F1BXH9WLBX28B5BSE7W
MRLH96X43SDKL59YX02YPJX5T1J751LCT71FW03554G7XJ3JEMKC0DAHLRR7TLG24HK6394SAD9L2LFPBE3PK2ZFXH8GW2S77NW096LCGQKSCDKZS90NZBF6
RW82H4LN4NFA61F8ND43K8YNYGNHNXNR4MXL0YLCRYF6TSKTF8LEL5E5WDX9N2W41H2Z4R1JYZYWMS0941Q283R14X24JZ5SEY8WYP7T03FWW6G6BXLA8L7G
Y5DK2ARP689B5ED94RG66BWX8PLLEHT5X5G4HGHNXS71FG23ZAFQ2J914QM2GNY65ZR2CH2QQ5N2W28SE94P76RYS8X5MF4CB4QG5TFDQFPTK57XNYW6727T
1MPPTZBELA173ZZ770CTT0GTZS3PC01L9RDFHHRWPTL7KW21KQ27XSC6RHSDC6SM7FTNB9W7FN2DW45BZRX6SPWAGCFR0EM8YN8C910H1S9XPPM0NT8SY1YZ
EGHYTXP9X55ZQ9DNBMX751QL2L0XMZ773N3EX78JJDLHMAHS47TXCRY85GTC3CGZCNTQ7RZYEWJBC59T4FZTFHN1DTE45TLS18J3QLDCPWG3NJL4CWSNAS10
BMDJPSNR44CGX4R27ZM7L3RS5AKH163C4HWZQWHG6135S3RTY8C5ADZHFMEG4JY5M22EZ61P3DEW4L0J3R0XDB7RSHZP5HL37N4N6KQXXC4BKCTWDH4JYRKA
H5SXX085F05MLE1RJK3P2E9Q9AZ26MEN3F2QA1D5HA3WGD2EXMD478GE4CPRADNXNW9SX8Z7JRLEENSNMFHK9CX6426PPAWQCGJP2L6B5HH6HQ6XWPCG3P81
WJYTRXYDAKCD00ZT3DCY3FF5XN98PYNTSYXSFFFLDPARX898B5P012902029Z2XMXJHSDBGJ6MKLEBC4JBCAMMMY9YWKF4ZK8DRBZG1PW6EQ2JG31YXQQ1N1
9W6H7Q8S389Y2XNHLD0RWBCFCQ27NW39ZXSPW4J506MSL587627DMGRAX2WXHMZD1M3XF916CQTSERTG2D11Y1B0FLS3BMHDD1NR2Y6QPCJCC6Y80TNZW73J
HWTYRSED6DELLGCDY9JDDSBR77HE2YX7XZBXM4NJWDXP1PCTT9M5BKMF1FY0GF8ABALSPEPTS2Y7WEC08A9AYN59FM469G9EMQF5CXP2JR5BESQ31YX9YYF0
838RNRLMQ1CB0B5EBBH4W01FZG44Z3DFG70GB12A98WJAR22377MQC9FRA3H24BARP8BY122NHBA4MDTJZPZY1B1BGJ5DTHDYFH1Z7FZNQ84L7SXNK7R6XT2
AAYF34RCC4LYR1HRR0JLQPR84B8NMN24AXC2BCQG9DH9LLWL176N67MNXENTEWS0H2SYQ4HYWE2P6HEBNZBAPKA2XS0NC50TYL9PXSRH0XZM7TS7J99473BM
2QM8EWNLN9EX3LBPRN5BTGX6KZN1Y7PFA59GN99PF8WQCL8CLDZ8Y6JAPJ7N16XBHHPHA1ZLPAD3ZBZ9ZCY40KPXP6XRWKBAM7P430XB5N0W73GNHZH3MZQL
Q9517KBK178D89HDT38CGX3C61HJ40XZJ1JYATABCJZ76EG34XHEJ1KR9MJ6WCP7L43ZA2NXBZMXTW8SHF05GEMK7G1LRRH5JYWNX9X8S1TBXRWB0JJB2G95
B27BXNK3YB3FL8WGP0PWGT5BAFFD64A27WMM9ZSWNWJLYBTRPY2F9R83FW4M6P12TPYF06J0D81LAW5K2REGRBR054WHHX7S2BEK2XEPTME3R69X78A7A6QH
N1LHGH8AR3YX852R3N33658WDJL0YD3Y5J4950CMPDTMA0M2FS85ZLR1FLZ0SYGGKDK0Y2WRQFMK6CCMX6C6HC3BSYPY1FHARMMY1RYN08K87WD5E44991LB
TD3WR1CRP61R79NQRKLBBSJHPMA1QZDY142TKQ94K24TG4RZ8LNS0JQX0KGX5YFQJKRQH9G8ZDNCWNYYEYA1KKGLPBYE3G39LCYW5H12TY8FYPGLNM8KZ200
MG1ABD3J4CBF1CK2DZKE0L603KLRC1GA4H119DS6495S5GD632ZPRPF34YCTTYBA4D7AJTT8FP41F93PZRQZPWYZ2DKGETM2MKD07M2M57PTD7Y1BPJ6TKPG
X43NAA2HRPC7L8KBS4DFYEG3DLF5FXWKB5K2H37H7GR4XK1DQLNN6GB0TQ9Z90ML9GRSR7QBQP1WJ98ZQ8GK2NLGQQCY2A5Z858QJLAHEBA2EJACG82A8NML
8AMTTCJNAGQN9JHQ1PTMH3ADAEFJD9FB3TDLL2LGSCG39D6X3CT1Z3LQ4YZ9WBLMDSBAC48X92QFPSS72B3A0KJCA5MCJB629XDM371L5N1EK7ZCXYXSSCXY
S1ENDZW868DFK5Y059YGD4RPKXJNPCYKL5JSWBHES9YRN1GLXH9P5980HZ0GYGL5WFZG8ET4Q948XECJ95HPP0FZ3ACD8K6YEQHBCK17BKS8X2GHYDZNY7ZD
9YJYAR10PJPBQRF6SEJENL9RASR79ZG0HEGPLHFE524EX3GDJLKJ7BJ9WKE0M16FM82BFJH135M8K27PRTH7EMEQZS226EJ4B31QTKRE0MBE1FD1H939R4YZ
QF6SRRQ33N7LJ7AEK2AAJF7540N4CRLBFZ90H1N1168A2403B52115QKT8Q5QCK217SK3S6D1J0FM6QSH97KNSNT48QZ7H6CR1WC6DJ32DP10863YWWRC9TT
RYKBJYGK9SWDF74JZZSBX1GFXK0Q74YBJA0Q7BHD96K428MAFHWSJSECN1B7KCXPXA85W060M1CYN66XPED4FSTF7EM1JTY3BQSL2T11WLT9ZCZGKTDDB5RX
6MGYPZD6FXHKGKAKFBBBZNN5TC6DA7N9J0W4B8RQ9HHJMQP966LP6EL3KH4RN494C05Z2M0GX3LT7DFWG1T8M86GFRGF4K39LSSNZLG7JTCYMNXWYZY8FBXA
B6L1YJ25NC5ZK9PPF3RTBFFC7G7MP547H23438F36TR1FPYSBY7008NQ27L942D544X10SDM7EP8CRHGYNBZM701212B0PJCZCEBXSSMFKDNKTGATPQN62YL
FF4AX8F66YXLAE35FH5DKW07T26C4BKC8QX9LSDHAP2TDMPNXEDDQYGYXQDBAXZJ976PT0GBFADNHCNBJDZHEC4R7WB9HN6A79KC45G3Z13EEBHXZE79JY9Y
XLW7LDWH8B10K4N5KB7F0NS3D9LWWQ24CHRK9WMR5P00Z09MPDTXD7QKEC3QB8ZZFZMHADHH3CCRN5MQ6NWG71LZLJ04GBE7R7NAGHPRTBRTK8RYM2HJ0W51
BTSNDF6D860XPXX1MFWGW2HRHEK3P0E7HF69AWMJD93CZQEX2QAZTPHSCMW3WPF625CDN6WDCM41LWGRTMHX5EDSBQ2LMJHY01L0BNRZ5JJBBEBDBRPJLCN4
7WXKBBSPCGXZ0FD9CWLGPJ16KYRFDRCZ2P556ZE03R3NFH9NAQGKZ415JTT2TL60DH9GEQ7JQF423AW2KKC58L3Z8E6R6900SJBFZFD0S9KWCBH29TRYQC4L
JNLRTTGBAPQQ2B2DL3QSGGCKJDAZ4NW8PPQ11FDB2BA40DW14T7EJ0X8RLFJE4GDHFLKSSTDW3WJHZA5J96QJ55JB1QHSFTQSQHRJKE79Q1AF7HDENB25J3J
S6MY5NPZ8GZG5TPNYRS0BQYH837CTF9B5R33NBRMPH2LS9EAW0M528X165L3TPGPPN7QBBL6RNHA99TG4P9BMZC42ST0J4X44MGYKTBKTRYX99A61C2PYQZX
4JA88E007BG1EP6MNBTTE3CMZ8L4LWBX729KPJL6TT3M1FDY85WBSMKMDFFAJQLYTQA1ZZ3JJGGA6Y6K8ETH77FZ8S8GJCHA7PBN00JE7E02583CBLTFHZ7F
8S6G2N85QSL25XPJRFB59GALTBYSRBL8WZXHGT2FBRJJF48F3ATL8B6R0BS0002J3J5R6YF2YC3GT66E9C6RGWRB5APT59AS7HSWJEHSK157B4HLTP549MYP
HNYX97DDRFQKR0NSHLTR2K50JBD6KLA8L7TQXM3FT7D9QCB0KEGCX9N3LCF0K5WF402BW8QFKB4HG1355RSY9SDQXRXD5HRXC28MT597J6RLBCBKC9S53FE8
FR7TH7GT5XNPY9ASCA0ZFQ5P1ZLE76B86FNN8G1C6YBB2298EJFRH1PK4DXY0MQ89QMRXQD5NHJW661GYGG4408MYT8GN529DY9NNMLNXXW55X9M6K0REWML
0E906Z4G4604JTHCGASWL3RH9GHQKFCLRMT72SQBTXYAG4X3ND5E4M9AWAJ3LWKLY6HFTG82APD8ZNAZDHT9XFC4LWT6G6HH9LBXTA43KW6MWT28S0YEADWG
8FQX507N1F2WLBMFL4TNA0MY7QL93PK6XHL134YE3FQJ8LDC253D9ALDJ689AHCLLKMJQQX7W91YPLD9B48EPCS4FHA5EX7ADJR3ZXFAHZ6G9GKLDHACTY9Z
J32XQLL3FLZBYBWJNELTT08MCJALSCWPHZW0WPN4EG15PFFQR1Y9ZPNY4SMQ2RM7ZY8AR8NEXY62GM9GQ2J09JZ6P2KW2ZE6H3TH61WL9ENJB9LAGFMJC2R3
HSM7MBWFG0B8Z5CS6CNX5EE9YB7E5B4290LNQNFXF89BD5WGS9AZQGKPPTCED9NM4LMGLT6YRCCDAM11R74QQQMMR6BDCB7TJJ4XAR06CN0FMHGMNDLQ3BL3
244Q1RRSJJDRJY6WR848FEEFSAWW2PM4HFGJFZPDLCDFDGJEXMBKB4E9NQ0WM96XHQC3FBHYZ9G1JEM37KWFQQH8M3YC5BPX6B85008B42D8P6661E5NC28A
8PFC7XPWMMNW9M561AKR9172M42ABTTSYZXB625JYLB23JLDR0E9ZZZ2L6D43MPD2WHLH2F4N8B3Z44HCWS1S8F0JRD9M3M1MX8X4934Y0T4DSL43GK22EK3
E2LB83CD86PGD7ELQM2BZNYY0AXS0DW1PNC5B4AARS8E9BAYSD38DDPR0WA7QC3M3H3G5X8MDE7M498P8657GEB7PW2M88D60H8TBENZA62PDT87CD2RSM7K
Y5GQC1SRK49T2TJD333HE6RKG2K5110TQBYBCABWPEF9MDMAHAPW9PBJWTFX26Z92HEQPHT8WZYRTQ6KKS37JG17JGMBQ2ET7AY6Q9K1SST7J3TQ2LS3T7FA
Q92MZNPNSRAJ9RSBSBFPGZ8S942FRJLP7NLPPDQJDJ88J4SN1EPH95Q0XTAX6GW2GP9FC1DJABGNYMN9BQALEK1N7BX2N4H0HSAT70A9Q89NETT4YB2LT0LD
QYQ8K0WM5DP4RSAQ438WER65BLBE1JKRK48NC15S5LQMGKLD9CQQCSDZ0H9H5BHGY00DH9F2Q21X3MNLA8CXHJ6C5C13ZJNHEF994HZG646XFAYLJERM27AY
G7D31C15N2NH6GDWD0HRM4LGW5WHJCJEGWJ4MSXMCXF0ANJ00M0WFTKJPBMHG0W3J71G9G25AJTT464D4D8C5Z9LNRWG0GNEP1T1H0QPDRYW7J8RWLCR9ML9
MPJW0JPFAYGQZXM1FQFMZW8AP6ATJZXGGCS7WJ8CK2B5JKQD426EZKGG1M4S23MAHZWALYF0X89CZY4LXRA7K8PADH1PYQKJFBN1CXZPTKBKA1FL15H6CG9J
CX4E9Q2462SFJM26Z518QZ3R216Q4DZ9PLQTCTSFGRAE8DZX649KL5KNQ18GLFJDYHMDKZR5HA700PAMHCZWS5W4QYEC0XJZ701T3TQ13AZPSQ587W1TRXAQ
00B7STP9HYX19W95CSE2J7TYEWWL4CCCRZGAJF3M08QJ11FE7YNYB114FX23PQHPFCSDPW6F1KC96PQWN8XBP0Y2AF8SWFKJNNWQWDWXX8Z550N5WXKTSSG1
L27NK1JJY7H2Z6J7J540T4TF36HWTCFQ8M8ZPG5RP2XGE3PXKHS757MK2X3GPPX5L92DKYEJXNERBDX99DDPZ6J65T8EY97MF57EMZ21GNTNCGT515LJ4XRF
ZQRHYBFGJM102FHAXKSP57L8J6G85Z9WFW60L5BX3CA8PQ424P9X6P9RSJ8Z5YS121HRQRX7CLK3CQM9MRZ4PKJKPG5EHZ56JX6ZX5PQ746GZJJ2J5B4018F
4CRLYMHK1NM0LLX4X5E60XLEY4WZSY7QEBD5S9G6TW8QP9G2NTEFGARHNWEKXH4NRW5A099HGRGFW3J9EX4TL4KE7FFMBCM3M32W5G69Z8HAAMCGTT48AQ3W
WPR53MKASCCZR0KH28QEE71MDSEFJJ4B44K10PB5BZW3BBY5SN7F739G2Q63HRGLJL8DDSAHMC1SZ9ZMTP9DB0YM2BS6B5HT7F5ZLZD315W4PS0YK4ETC5PQ
KJST082QWYFPHR7K5E8E0W5E77MGJ5XQ0DY6BFKL0EDCLGW7TYH4XXW5MY90MH29TFDES2ALNFKE7BHDCE9QRGDMZ8W2FHZHB19903QFP4YAY40EW432D4Y1
020A63Q2E1ZYD0G3CZXEESSPQ29372EQ41KRD4XSK6A2M886XTGR7MNAY9X3ZN7E11LLKRGP7MWJEYYRWZREXRGF9APKAAF7BY49YDTB7FTZGWZKSHFEH7YA
WNRCA0GWGD0ZKHR139JR05K52E67Z79S7W29JAGEFB9EYWMC00C7924ZPRP07T52FYZD63KMGB7ZLK1TLECQN9FQ9XXDG1P7TGG3NLA0J93X7DGHSYGKW640
53Y9DNE5G1S08Y7G20ALR82SQAR194HPT4T54MBDQ9YL23THP3WN6HKKQS61PPRYHTMLPSA8B6C5X5X2K4W1L3ADW9LTT59KPR0QS5LW4KR3GXM5BR8JCJ2M
6RN359WE9J330K5CNMZ012TDXNWQJZLRHEC28MNYG3C7WA09HBJKM6216HRRDLR96DGK7GSQRXSWL5GWJEBGGMH8X48N2QTEBJXFEEWENCPESFPNR70GK3XC
1T30YL1CX0PR1TEY5D5GNL0N46G8F67Z303D1D40RZ01K190LN7ELQE2STTE3C9ZNL1YSKZAMB7ZDEBAEFGAW1E32MJ866XAZG7M5PH37RRZL38AQ2TT08EK
E2ZGL789QKRDJ44YJ44XT0LRKQTLL80ATB88FEYWFY8CK0EQDWW5KNMN8NECMYFPC95PQ8TWPR84GBZ15AQ0SE0HDYT6TZALKHDHFM628JB2TN0QS2XRNA4C
6A038MKHXXLKTZCN5G2Q0KM53TM3BFZ2L38CSH1E7MXCXPCYF8P6HLH18XDASTDGEBR0R1YBP0ZWXYXSPW1GNJH83942XWSN5S0M7ZK6BME8QNHM9J29Q7R7
DJLP7GP45FFJ7EYF7L9SEAHQR7YQ21SPSGQ9BMAK95PD6NN2E9DACTJCDSBP7989KLFT357LBAAXSA40C9MHFGTN5DQJ856ZB5K031T0BBSW2QJ8100GBH0J
TXQNYKZCG5NNQT0AE5CPDWM5K99TSJ673K7DNP40L40DA6F0HNF99TPBFTDEAGHXLRNENH1E5ZSZPE3ZEQSQYKA06FY97JZHJGW4DX267CP6K7QXAJ96QCMK
NQ76HNP5G9F1ZKBN26A32KQDQ10MEWGDTP0KPFG0H7ZETRD060NEL8KDNL6DPQK18EAMR89HSK97K8812MMNEGDE90KTXPWTDDPE7TW97GD12BW4SRHFZTYK
4S6BKDEXJDN159X4KPS363113KZR7HSRQ1345JB0WBZRL74FAPP97YX0S7G9R5WQQ5ZD0RRJBHJJN8CA5B3JCQDXS1ZSQSJ5ZLABL6TJPA8PJ7KFFWCP1JW6
AATJ1A0JZMTW427HESG2T5KHN2GY3SB2YE3T0PABLBLXM477PP51EGJDSELWFJHG2Y4SRNW5BNYS439ZBP4H0M9LBPNQHZX7439J3000FZL3RPRBQJY20WNP
9NMAG19EDFXH6SC5MSS8JAAZN7A0EAXQS1JMW6BQM8G4HRFFALWRGDH0GP7TK5RRQJMTYSRQHZZLZ71QHXQDNGYGFB4DKJE4QBSP9A8A8XGFMHWFBTN74RQ3
BZ4952AD5YCN76SK55D3XQQMYQKJQH96E6C7EXG60YA8BYHJ8TTTYYP1P4S853K7XW2R4A7D79BX2R3T9L2WMWX57ECJJ21CZ0CLM0SJJ9339ZE0WNJRWCML
K6FQEZ7TB0QK74QY67DDPSR9SK93WNKBAD6GTWJDKZXTLWWZE8TDH9M87N6LNYJALRM820BC3MLJRP5EMPSX1Z9CZCKZEQ8APEJW1TMDP32PBRDKPFDY7MDP
A2G09XM1NGF14T5HLRZJYTQRZBYPSEQYNXZ9JXXJ6KAN7ZHHHD545E4W4K9QFYNX0GZE66291E93TS1KXQQ13ZX15ZR14NGCGCJ803YZ42YACTYPRPRCGFL9
7LXRL6DT0WR7M1NFFR5N9MPW5NM9XB0MJA8XT7Z4LZRBJNQCNDC97B047W6MLMDFH7SS2MQRXQB3QAHQ01Y8DGGZS820DP69ATBEP6E2SH84P0GFZH9TDKJX
Z3AT79XWX8QWJ3HN1FHYAD0Q9JYMQKT0WBHLE7PNKMWPA6Q7MG341NG24ZYZM9BYLR56BEA9BDTW0RL9ZJA4487MF329LMW78NWSDD3ZJEAEEGQ9EJAWZ9Y7
5XDBTQXN8HSBTWR6QAGCMF5BDHL1AMCZ1JNWZ2WX738D58GD43J0QFQBBDCW64A3MSMXDRKG3Z1SXHF3YCNNP877BGL2DGM7MC56D0QH3RQQHW6JMA45JYJ1
ZF0T79CJMAY65NNKN8HHYN5QAFDR0213JZEYSJSHRAP5SPGSMD8QDC5FN600EHPXEDFF2EB2G7R5AZG4TXG5P9TQDH2SEWDS8NL1EJF1Y0Q73FREKG64K3Z2
16CMD4ZB4WC27N0BF2FPKFCP7FH1PD0Y0R8YJF3M4H1RZZ3B6Q6Y2FRLAH8S92AJ2JKBC9STS698QLSSCTAQHACQP7J1LRSNJRBW99F3C4AB219QWC5AXAK2
9PFPPLPHL66PC5B4F2YJF2ZJ82R9DKG8475H4LLXZZ2Y25KJRA80DFDBKDL2X9T3EQYWD541W79ZJ7LJWCEQNKAJXZ3KKEWXC244Y5KXJB78X4G4TTGT78AM
X2GDNKP3CH1E26NB26AJN8MTNWX61L40RERNXNBPZDC603XSFENFBMH7Y72TH7HHZ4Z7HRAEEXGYQ66WGA4NHL744LSL4Q2EG6HC7QKZWL41HNFDA3CPKGHQ
TQDS5Q09S3MT7GJE9MMS5TFHLPGFFCWH78QJ526G8J4SL8FPD9J6XCPR300PNKG7ZFY2KBAZTP885W56009SRTSCHLBHFNNBXYMCCH1LZPC29AE45QQMRJA4
]] , '%s+', '')
--*****************************************************************************
function preload_1010()
-- port from golang: https://golang.org/src/time/time.go
--
local secondsPerMinute = 60
local secondsPerHour = 60 * 60
local secondsPerDay = 24 * secondsPerHour
local secondsPerWeek = 7 * secondsPerDay
local daysPer400Years = 365*400 + 97
local daysPer100Years = 365*100 + 24
local daysPer4Years = 365*4 + 1
local unixBase = (1969*365 + 1969//4 - 1969//100 + 1969//400) * secondsPerDay
-- daysBefore[m] counts the number of days in a non-leap year
-- before month m begins. There is an entry for m=12, counting
-- the number of days before January of next year (365).
local daysBefore = {
0,
31,
31 + 28,
31 + 28 + 31,
31 + 28 + 31 + 30,
31 + 28 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
}
local January = 1
local February = 2
local March = 3
local April = 4
local May = 5
local June = 6
local July = 7
local August = 8
local September = 9
local October = 10
local November = 11
local December = 12
local Sunday = 1
local Monday = 2
local Tuesday = 3
local Wednesday = 4
local Thursday = 5
local Friday = 6
local Saturday = 7
-- absTimestamp returns the absolute second from {year=1, month=1, day=1, hour=0, mintue=0, second=0}
local function absTimestamp(ts)
return ts + unixBase
end
local function isLeap(year)
return year%4 == 0 and (year%100 ~= 0 or year%400 == 0)
end
local function absWeekday(abs)
-- January 1 of the absolute year, like January 1 of 2001, was a Monday.
local sec = (abs+1*secondsPerDay) % secondsPerWeek
return (sec // secondsPerDay) + 1
end
-- convert timestamp to year, month, day, yday
local function absDate(abs)
local day = abs // secondsPerDay
local n, y
-- Account for 400 year cycles.
n = day // daysPer400Years
y = 400 * n
day = day - daysPer400Years * n
-- Cut off 100-year cycles.
-- The last cycle has one extra leap year, so on the last day
-- of that year, day / daysPer100Years will be 4 instead of 3.
-- Cut it back down to 3 by subtracting n>>2.
n = day // daysPer100Years
n = n - (n >> 2)
y = y + 100 *n
day = day - daysPer100Years * n
-- Cut off 4-year cycles.
-- The last cycle has a missing leap year, which does not
-- affect the computation.
n = day // daysPer4Years
y = y + 4 * n
day = day - daysPer4Years * n
-- Cut off years within a 4-year cycle.
-- The last year is a leap year, so on the last day of that year,
-- day / 365 will be 4 instead of 3. Cut it back down to 3
-- by subtracting n>>2.
n = day // 365
n = n - (n >> 2)
y = y + n
day = day - 365 * n
local year = y + 1
local yday = day + 1
if isLeap(year) then
-- Leap year
if day > 31+29-1 then
-- After leap day; pretend it wasn't there.
day = day - 1
elseif day == 31+29-1 then
return year, February, 29, yday
end
end
-- Estimate month on assumption that every month has 31 days.
-- The estimate may be too low by at most one month, so adjust.
local month = (day // 31) + 1
local monthDayEnd = daysBefore[month + 1]
local monthDayBegin
if day >= monthDayEnd then
month = month + 1
monthDayBegin = monthDayEnd
else
monthDayBegin = daysBefore[month]
end
day = day - monthDayBegin + 1
return year, month, day, yday
end
local M = {}
M.TZ = 8
function M.localdate(sec, tz, t)
local tz = tz or M.TZ
return M.date(sec + tz*secondsPerHour, t)
end
function M.date(sec, t)
local abs = absTimestamp(sec)
local t = t or {}
t.year, t.month, t.day, t.yday = absDate(abs)
t.wday = absWeekday(abs)
local seconds = abs % secondsPerDay
t.hour = seconds // secondsPerHour
seconds = seconds - (t.hour * secondsPerHour)
t.min = seconds // secondsPerMinute
t.sec = seconds - (t.min * secondsPerMinute)
return t
end
function M.date_reverse (year,month,day,hour,min,sec) -- cgabriel 2021
local days = (year-1970)*365 + math.floor((year-1968)/4)
leap_year_days_off = 1 + math.floor((year-1900)/100) - math.floor((year-1600)/400) -- cgabriel 16.7.22
leap_year_before_march = 0
if month < 3 then -- cgabriel 3.2.24
if (4*math.floor((year-1968)/4) - (year-1968)) == 0 then -- leap year
leap_year_before_march = 1
if (100*math.floor((year-1900)/100) - (year-1900)) == 0 then -- no leap year
leap_year_before_march = 1
if (400*math.floor((year-1600)/400) - (year-1400)) == 0 then -- still leap year
leap_year_before_march = 1
end
end
end
end
leap_year_days_off = leap_year_days_off + leap_year_before_march
days = days - leap_year_days_off
days = days + daysBefore[month] + day
local secs = 86400*days + hour*3600 + min*60 + sec
return secs
end
function M.date_format (format_string,sec)
local t = M.date(sec)
format_string = string.gsub(format_string,'%%y',string.format('%04u',tonumber(t.year)))
format_string = string.gsub(format_string,'%%m',string.format('%02u',tonumber(t.month)))
format_string = string.gsub(format_string,'%%d',string.format('%02u',tonumber(t.day)))
format_string = string.gsub(format_string,'%%H',string.format('%02u',tonumber(t.hour)))
format_string = string.gsub(format_string,'%%M',string.format('%02u',tonumber(t.min)))
format_string = string.gsub(format_string,'%%S',string.format('%02u',tonumber(t.sec)))
return format_string
end
function M.date_format1 (format_string,sec)
format_string = string.gsub(format_string,'%%y',string.sub(sec,1,4))
format_string = string.gsub(format_string,'%%m',string.sub(sec,5,6))
format_string = string.gsub(format_string,'%%d',string.sub(sec,7,8))
format_string = string.gsub(format_string,'%%H',string.sub(sec,9,10))
format_string = string.gsub(format_string,'%%M',string.sub(sec,11,12))
format_string = string.gsub(format_string,'%%S',string.sub(sec,13,14))
return format_string
end
local tmp = {}
function M.format(sec, utc)
if utc then
M.date(sec, tmp)
else
M.localdate(sec, nil, tmp)
end
return string.format('%4d-%02d-%02d %02d:%02d:%02d',
tmp.year, tmp.month, tmp.day, tmp.hour, tmp.min, tmp.sec)
end
-- test functions
--
function M.run_random_test()
for i=1, 100 do
local ts = math.random(0, 2^31)
local t1 = os.date('!*t', ts)
local t2 = M.date(ts)
for k,v in pairs(t1) do
if k ~= 'isdst' then
assert(v == t2[k], k)
end
end
end
end
function M.run_full_test()
local total = 2^31
for i=1, total do
local t1 = os.date('!*t', i)
local t2 = M.date(i, tmp)
for k,v in pairs(t1) do
if k ~= 'isdst' then
assert(v == t2[k], k)
end
end
if i % 100000 == 0 then
print('------------------------:', total, i)
end
end
end
function M.run_profile_test(times)
local times = times and times > 0 or 1000000
local from = math.random(2^31 - times)
local to = from + times
local t1 = os.clock()
for ts = from, to do
os.date('!*t', ts)
end
local t = {}
local t2 = os.clock()
for ts = from, to do
M.date(ts, t)
end
local t3 = os.clock()
print(string.format('%d times call %s cost %s seconds', times, 'os.date', t2-t1))
print(string.format('%d times call %s cost %s seconds', times, 'M.date', t3-t2))
end
return M
end
date1 = preload_1010()
--************************************************************************
function preload_1005()
------------------------------------------------------------
-- rc4 encryption / decryption
local byte, char, concat = string.byte, string.char, table.concat
local function keysched(key)
-- key must be a 16-byte string
assert(#key == 16)
local s = {}
local j,ii,jj
for i = 0, 255 do s[i+1] = i end
j = 0
for i = 0, 255 do
ii = i+1
j = (j + s[ii] + byte(key, (i % 16) + 1)) & 0xff
jj = j+1
s[ii], s[jj] = s[jj], s[ii]
end
return s
end
local function step(s, i, j)
i = (i + 1) & 0xff
local ii = i + 1
j = (j + s[ii]) & 0xff
local jj = j + 1
s[ii], s[jj] = s[jj], s[ii]
local k = s[ ((s[ii] + s[jj]) & 0xff) + 1 ]
return s, i, j, k
end
local function rc4raw(key, plain)
-- raw encryption
-- key must be a 16-byte string
local s = keysched(key)
local i, j = 0, 0
local k
local t = {}
for n = 1, #plain do
s, i, j, k = step(s, i, j)
t[n] = char(byte(plain, n) ~ k)
end
return concat(t)
end
local function rc4(key, plain, drop)
-- encrypt 'plain', return encrypted text
-- key must be a 16-byte string
-- optional drop (default = 256): ignore first 'drop' iterations
drop = drop or 256
local s = keysched(key)
local i, j = 0, 0
local k
local t = {}
-- run and ignore 'drop' iterations
for _ = 1, drop do
s, i, j = step(s, i, j)
end
-- now start to encrypt
for n = 1, #plain do
s, i, j, k = step(s, i, j)
t[n] = char(byte(plain, n) ~ k)
end
return concat(t)
end
return { -- module
rc4raw = rc4raw,
rc4 = rc4,
encrypt = rc4,
decrypt = rc4,
}
end
rc4 = preload_1005()
--*****************************************************************************
function preload_1006()
local spack, sunpack = string.pack, string.unpack
------------------------------------------------------------------------
local function FF(a, b, c, d, x, s, ac)
a = (a + ((b & c) | ((~b) & d)) + x + ac) & 0xffffffff
a = ((a << s) | (a >> (32-s))) & 0xffffffff
a = (a + b) & 0xffffffff
return a
end
local function GG(a, b, c, d, x, s, ac)
a = (a + ((b & d) | c & (~d) ) + x + ac) & 0xffffffff
a = ((a << s) | (a >> (32-s))) & 0xffffffff
a = (a + b) & 0xffffffff
return a
end
local function HH(a, b, c, d, x, s, ac)
a = (a + ((b ~ c ~ d)) + x + ac) & 0xffffffff
a = ((a << s) | (a >> (32-s))) & 0xffffffff
a = (a + b) & 0xffffffff
return a
end
local function II(a, b, c, d, x, s, ac)
a = (a + (c ~ (b | ~d)) + x + ac) & 0xffffffff
a = ((a << s) | (a >> (32-s))) & 0xffffffff
a = (a + b) & 0xffffffff
return a
end
local function transform(state, input, i, t)
-- process the 64-byte input block in string 'input' at offset 'i'
-- t is a uint32[16] array. It is passed as a parameter
-- for performance reasons
--
local a, b, c, d = state[1], state[2], state[3], state[4]
-- load array
for j = 1, 16 do
t[j] = sunpack('<I4', input, i)
i = i + 4
end
-- Round 1
a = FF (a, b, c, d, t[ 1], 7, 0xd76aa478)
d = FF (d, a, b, c, t[ 2], 12, 0xe8c7b756)
c = FF (c, d, a, b, t[ 3], 17, 0x242070db)
b = FF (b, c, d, a, t[ 4], 22, 0xc1bdceee)
a = FF (a, b, c, d, t[ 5], 7, 0xf57c0faf)
d = FF (d, a, b, c, t[ 6], 12, 0x4787c62a)
c = FF (c, d, a, b, t[ 7], 17, 0xa8304613)
b = FF (b, c, d, a, t[ 8], 22, 0xfd469501)
a = FF (a, b, c, d, t[ 9], 7, 0x698098d8)
d = FF (d, a, b, c, t[10], 12, 0x8b44f7af)
c = FF (c, d, a, b, t[11], 17, 0xffff5bb1)
b = FF (b, c, d, a, t[12], 22, 0x895cd7be)
a = FF (a, b, c, d, t[13], 7, 0x6b901122)
d = FF (d, a, b, c, t[14], 12, 0xfd987193)
c = FF (c, d, a, b, t[15], 17, 0xa679438e)
b = FF (b, c, d, a, t[16], 22, 0x49b40821)
-- Round 2
a = GG (a, b, c, d, t[ 2], 5, 0xf61e2562)
d = GG (d, a, b, c, t[ 7], 9, 0xc040b340)
c = GG (c, d, a, b, t[12], 14, 0x265e5a51)
b = GG (b, c, d, a, t[ 1], 20, 0xe9b6c7aa)
a = GG (a, b, c, d, t[ 6], 5, 0xd62f105d)
d = GG (d, a, b, c, t[11], 9, 0x2441453)
c = GG (c, d, a, b, t[16], 14, 0xd8a1e681)
b = GG (b, c, d, a, t[ 5], 20, 0xe7d3fbc8)
a = GG (a, b, c, d, t[10], 5, 0x21e1cde6)
d = GG (d, a, b, c, t[15], 9, 0xc33707d6)
c = GG (c, d, a, b, t[ 4], 14, 0xf4d50d87)
b = GG (b, c, d, a, t[ 9], 20, 0x455a14ed)
a = GG (a, b, c, d, t[14], 5, 0xa9e3e905)
d = GG (d, a, b, c, t[ 3], 9, 0xfcefa3f8)
c = GG (c, d, a, b, t[ 8], 14, 0x676f02d9)
b = GG (b, c, d, a, t[13], 20, 0x8d2a4c8a)
-- Round 3
a = HH (a, b, c, d, t[ 6], 4, 0xfffa3942)
d = HH (d, a, b, c, t[ 9], 11, 0x8771f681)
c = HH (c, d, a, b, t[12], 16, 0x6d9d6122)
b = HH (b, c, d, a, t[15], 23, 0xfde5380c)
a = HH (a, b, c, d, t[ 2], 4, 0xa4beea44)
d = HH (d, a, b, c, t[ 5], 11, 0x4bdecfa9)
c = HH (c, d, a, b, t[ 8], 16, 0xf6bb4b60)
b = HH (b, c, d, a, t[11], 23, 0xbebfbc70)
a = HH (a, b, c, d, t[14], 4, 0x289b7ec6)
d = HH (d, a, b, c, t[ 1], 11, 0xeaa127fa)
c = HH (c, d, a, b, t[ 4], 16, 0xd4ef3085)
b = HH (b, c, d, a, t[ 7], 23, 0x4881d05)
a = HH (a, b, c, d, t[10], 4, 0xd9d4d039)
d = HH (d, a, b, c, t[13], 11, 0xe6db99e5)
c = HH (c, d, a, b, t[16], 16, 0x1fa27cf8)
b = HH (b, c, d, a, t[ 3], 23, 0xc4ac5665)
-- Round 4
a = II (a, b, c, d, t[ 1], 6, 0xf4292244)
d = II (d, a, b, c, t[ 8], 10, 0x432aff97)
c = II (c, d, a, b, t[15], 15, 0xab9423a7)
b = II (b, c, d, a, t[ 6], 21, 0xfc93a039)
a = II (a, b, c, d, t[13], 6, 0x655b59c3)
d = II (d, a, b, c, t[ 4], 10, 0x8f0ccc92)
c = II (c, d, a, b, t[11], 15, 0xffeff47d)
b = II (b, c, d, a, t[ 2], 21, 0x85845dd1)
a = II (a, b, c, d, t[ 9], 6, 0x6fa87e4f)
d = II (d, a, b, c, t[16], 10, 0xfe2ce6e0)
c = II (c, d, a, b, t[ 7], 15, 0xa3014314)
b = II (b, c, d, a, t[14], 21, 0x4e0811a1)
a = II (a, b, c, d, t[ 5], 6, 0xf7537e82)
d = II (d, a, b, c, t[12], 10, 0xbd3af235)
c = II (c, d, a, b, t[ 3], 15, 0x2ad7d2bb)
b = II (b, c, d, a, t[10], 21, 0xeb86d391)
state[1] = (state[1] + a) & 0xffffffff
state[2] = (state[2] + b) & 0xffffffff
state[3] = (state[3] + c) & 0xffffffff
state[4] = (state[4] + d) & 0xffffffff
end --transform()
local function md5(input)
-- initialize state
local state = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 }
local inputlen = #input
local inputbits = inputlen * 8 -- input length in bits
local r = inputlen -- number of unprocessed bytes
local i = 1 -- index in input string
local ibt = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} -- input block uint32[16]
-- process as many 64-byte blocks as possible
while r >= 64 do
-- process block
transform(state, input, i, ibt)
i = i + 64 -- update input index
r = r - 64 -- update number of unprocessed bytes
end
-- finalize. must append to input a mandatory 0x80 byte, some
-- padding, and the input bit-length ('inputbits')
local lastblock -- the rest of input .. some padding .. inputbits
local padlen -- padding length in bytes
if r < 56 then padlen = 55 - r else padlen = 119 - r end
lastblock = input:sub(i) -- remaining input
.. '\x80' .. ('\0'):rep(padlen) --padding
.. spack('<I8', inputbits) -- length in bits
assert(#lastblock == 64 or #lastblock == 128)
transform(state, lastblock, 1, ibt)
if #lastblock == 128 then
transform(state, lastblock, 65, ibt)
end
-- return the digest
local digest = spack('<I4I4I4I4', state[1], state[2], state[3], state[4])
return digest
end --md5()
--~ print(bin.stohex(md5'abc'))
--~ print(bin.stohex(md5''))
return { -- md5 module
hash = md5,
}
end
md5lib = preload_1006()
--*****************************************************************************
function preload_1008()
local byte, char, concat = string.byte, string.char, table.concat
local b10chars = '0123456789'
b10chars = '1375629804'
local function encode(s)
if #s==0 then return string.sub(b10chars,1,1) end
local q, b
local et = {}
local zn = 0 -- number of leading zero bytes in s
-- assume s is a large, little-endian binary number
-- with base256 digits (each byte is a 'digit')
local nt = {} -- number to divide in base 256, big endian
local dt = {} -- result of nt // 10, in base 256
local more = true -- used to count leading zero bytes
for i = 1, #s do
b = byte(s, i)
-- if more and b == 0 then
-- zn = zn + 1
-- else
-- more = false
-- end
nt[i] = b
end
-- if #s == zn then --take care of strings empty or with only nul bytes
-- return string.rep('1', zn)
-- end
more = true
while more do
local r = 0
more = false
for i = 1, #nt do
b = nt[i] + (256 * r)
q = b // 10
-- if q is not null at least once, we are good
-- for another division by 10
more = more or q > 0
r = b % 10
dt[i] = q
end
-- r is the next base10 digit. insert it before previous ones
-- to get a big-endian base10 number
table.insert(et, 1, char(byte(b10chars, r+1)))
-- now copy dt into nt before another round of division by 10
nt = {}
for i = 1, #dt do nt[i] = dt[i] end
dt = {}
end--while
-- don't forget the leading zeros ('1' is digit 0 in bitcoin base10 alphabet)
return string.rep('1', zn) .. concat(et)
end --encode()
-- inverse base10 map, used by b10decode: b10charmap maps characters in
-- base10 alphabet to their _offset_ in b10chars (0-based, not 1-based...)
-- eg. for digit '1' b64charmap[65] == 0 and for 'z', b64charmap[122] == 57
--
local b10charmap = {};
for i = 1, 10 do b10charmap[byte(b10chars, i)] = i - 1 end
local function decode(s)
if s==string.sub(b10chars,1,1) then return('') end
-- reject invalid encoded strings
if string.find(s, '[^'..b10chars..']') then
return nil, 'invalid char'
end
-- process leading zeros - count and remove them
local zn -- number of leading zeros (base10 digits '1')
-- zn = #(string.match(s, '^(1+)') or '')
-- s = string.gsub(s, '^(1+)', '')
-- special case: the string is empty or contains only null bytes
-- if s == '' then
-- return string.rep('\x00', zn)
-- end
--
-- process significant digits
local dn -- decoded number as an array of bytes (little-endian)
local d -- base10 digit, as an integer
local b -- a byte in dn
local m -- a byte multiplied by 10 (used for product)
local carry
dn = { b10charmap[byte(s, 1)] } --init with most significant digit
for i = 2, #s do --repeat until no more digits
-- multiply dn by 10, then add next digit
d = b10charmap[byte(s, i)] -- next digit
carry = 0
-- multiply dn by 10
for j = 1, #dn do
b = dn[j]
m = b * 10 + carry
b = m & 0xff
carry = m >> 8
dn[j] = b
end
if carry > 0 then dn[#dn + 1] = carry end
-- add next digit to dn
carry = d
for j = 1, #dn do
b = dn[j] + carry
carry = b >> 8
dn[j] = b & 0xff
end
if carry > 0 then dn[#dn + 1] = carry end
end
-- now dn contains the decoded number (little endian)
-- must add leading zeros and reverse dn to build binary string
local ben = {} -- big-endian number as array of chars
local ln = #dn
for i = 1, ln do
ben[i] = char(dn[ln-i+1])
end
return concat(ben)
-- return string.rep('\x00', zn) .. concat(ben)
end --decode()
return { -- base10 module
encode = encode,
decode = decode,
}
end
base10 = preload_1008()
--*****************************************************************************
function preload_1008a()
local byte, char, concat = string.byte, string.char, table.concat
local b27chars = 'ABCDE'..'FGHJK'..'LMNPQ'..'RSTWX'..'Y'..'345679'
local function encode(s) -- encode byte array to 27 chars string
if #s==0 then return string.sub(b27chars,1,1) end
local q, b
local et = {}
local zn = 0 -- number of leading zero bytes in s
-- assume s is a large, little-endian binary number
-- with base256 digits (each byte is a 'digit')
local nt = {} -- number to divide in base 256, big endian
local dt = {} -- result of nt // 27, in base 256
local more = true -- used to count leading zero bytes
for i = 1, #s do
b = byte(s, i)
-- if more and b == 0 then
-- zn = zn + 1
-- else
-- more = false
-- end
nt[i] = b
end
-- if #s == zn then --take care of strings empty or with only nul bytes
-- return string.rep('1', zn)
-- end
more = true
while more do
local r = 0
more = false
for i = 1, #nt do
b = nt[i] + (256 * r)
q = b // 27
-- if q is not null at least once, we are good
-- for another division by 27
more = more or q > 0
r = b % 27
dt[i] = q
end
-- r is the next base27 digit. insert it before previous ones
-- to get a big-endian base27 number
table.insert(et, 1, char(byte(b27chars, r+1)))
-- now copy dt into nt before another round of division by 27
nt = {}
for i = 1, #dt do nt[i] = dt[i] end
dt = {}
end--while
-- don't forget the leading zeros ('1' is digit 0 in bitcoin base27 alphabet)
return(concat(et))
-- return string.rep('1', zn) .. concat(et)
end --encode()
-- inverse base27 map, used by b27decode: b27charmap maps characters in
-- base27 alphabet to their _offset_ in b27chars (0-based, not 1-based...)
-- eg. for digit '1' b64charmap[65] == 0 and for 'z', b64charmap[122] == 57
--
local b27charmap = {};
for i = 1, 27 do b27charmap[byte(b27chars, i)] = i - 1 end
local function decode(s)
if s==string.sub(b27chars,1,1) then return('') end
-- reject invalid encoded strings
if string.find(s, '[^'..b27chars..']') then
return nil, 'invalid char'
end
-- process leading zeros - count and remove them
-- local zn -- number of leading zeros (base27 digits '1')
-- zn = #(string.match(s, '^(1+)') or '')
-- s = string.gsub(s, '^(1+)', '')
-- special case: the string is empty or contains only null bytes
-- if s == '' then
-- return string.rep('\x00', zn)
-- end
--
-- process significant digits
local dn -- decoded number as an array of bytes (little-endian)
local d -- base27 digit, as an integer
local b -- a byte in dn
local m -- a byte multiplied by 27 (used for product)
local carry
dn = { b27charmap[byte(s, 1)] } --init with most significant digit
for i = 2, #s do --repeat until no more digits
-- multiply dn by 27, then add next digit
d = b27charmap[byte(s, i)] -- next digit
carry = 0
-- multiply dn by 27
for j = 1, #dn do
b = dn[j]
m = b * 27 + carry
b = m & 0xff
carry = m >> 8
dn[j] = b
end
if carry > 0 then dn[#dn + 1] = carry end
-- add next digit to dn
carry = d
for j = 1, #dn do
b = dn[j] + carry
carry = b >> 8
dn[j] = b & 0xff
end
if carry > 0 then dn[#dn + 1] = carry end
end
-- now dn contains the decoded number (little endian)
-- must add leading zeros and reverse dn to build binary string
local ben = {} -- big-endian number as array of chars
local ln = #dn
for i = 1, ln do
ben[i] = char(dn[ln-i+1])
end
-- return string.rep('\x00', zn) .. concat(ben)
return concat(ben)
end --decode()
return { -- base27 module
encode = encode,
decode = decode,
}
end
base27 = preload_1008a()
--*****************************************************************************
function preload_1008u()
local b27chars = 'ABCDE'..'FGHJK'..'LMNPQ'..'RSTWX'..'Y'..'345679'
local function encode(shex) -- encode byte array to 27 chars string
local hexbit = bin.hextobin(shex)
hexbit = table.concat(hexbit)
local encoded_block = ""
while 0 == 0 do
local bitsnr19 = 0
for i=1,19 do
bitsnr19 = bitsnr19 * 2
if string.sub(hexbit,i,i) == "1" then
bitsnr19 = bitsnr19 + 1
end
if i == #hexbit then
break
end
end
bitsnr19 = bitsnr19 + 0.001
print(hexbit,bitsnr19)
hexbit = string.sub(hexbit,20)
local encoded_block1 = ""
while 0 == 0 do
-- print(bitsnr19)
local pos = math.floor(bitsnr19 % 27) + 1
print("PP",pos)
encoded_block1 = string.sub(b27chars,pos,pos) .. encoded_block1
bitsnr19 = (bitsnr19 + 0.001) // 27 + 0.001
print(bitsnr19,encoded_block1)
if bitsnr19 < 0.5 then
break
end
end
encoded_block = encoded_block .. encoded_block1
if hexbit == "" then
break
end
end
-- print(encoded_block)
return(encoded_block)
end
local function decode(s27)
local decoded_bin = {}
while 0 == 0 do
local part_nr = 0
local len_block = 19
for i=1,4 do
local char_aktuell = string.sub(s27,i,i)
-- if char_aktuell == "" then
-- len_block = 5*(i-1)-1
-- end
part_nr = part_nr * 27
part_nr = part_nr + string.find(b27chars,char_aktuell) - 1
print(i,part_nr)
end
print("---------------",len_block)
local part_hex = string.format("%0x",part_nr )
print("QQ",s27,part_hex)
local part_bin = bin.hextobin(part_hex)
for i=1,len_block,1 do
local a = part_bin[i]
if a == nil then
a = 0
end
table.insert(decoded_bin,a)
end
-- print(">>>>",table.concat(decoded_bin),s27)
s27 = string.sub(s27,5)
if s27 == "" then
break
end
end
-- while #decoded_bin % 4 > 0 do
-- table.insert(decoded_bin,"0")
-- end
local result_hex_string = bin.bintohex( decoded_bin )
-- print(result_hex_string)
return(result_hex_string)
end
return { -- base27 module
encode = encode,
decode = decode,
}
end
base27precise = preload_1008u()
--*****************************************************************************
function preload_1008b()
local byte, char, concat = string.byte, string.char, table.concat
local b32chars = 'ABCDE'..'FGHJK'..'LMNPQ'..'RSTWX'..'YZ'..'0123456789'
local b32chars = 'RY1T9W2X'..'F0GZH4JK'..'SA67B3CDE'..'L5M8QPN'
local function encode(s) -- encode byte array to 32 chars string
if #s==0 then return string.sub(b32chars,1,1) end
local q, b
local et = {}
local zn = 0 -- number of leading zero bytes in s
-- assume s is a large, little-endian binary number
-- with base256 digits (each byte is a 'digit')
local nt = {} -- number to divide in base 256, big endian
local dt = {} -- result of nt // 32, in base 256
local more = true -- used to count leading zero bytes
for i = 1, #s do
b = byte(s, i)
-- if more and b == 0 then
-- zn = zn + 1
-- else
-- more = false
-- end
nt[i] = b
end
-- if #s == zn then --take care of strings empty or with only nul bytes
-- return string.rep('1', zn)
-- end
more = true
while more do
local r = 0
more = false
for i = 1, #nt do
b = nt[i] + (256 * r)
q = b // 32
-- if q is not null at least once, we are good
-- for another division by 32
more = more or q > 0
r = b % 32
dt[i] = q
end
-- r is the next base32 digit. insert it before previous ones
-- to get a big-endian base32 number
table.insert(et, 1, char(byte(b32chars, r+1)))
-- now copy dt into nt before another round of division by 32
nt = {}
for i = 1, #dt do nt[i] = dt[i] end
dt = {}
end--while
-- don't forget the leading zeros ('1' is digit 0 in bitcoin base32 alphabet)
return concat(et)
-- return string.rep('1', zn) .. concat(et)
end --encode()
-- inverse base32 map, used by b32decode: b32charmap maps characters in
-- base32 alphabet to their _offset_ in b32chars (0-based, not 1-based...)
-- eg. for digit '1' b64charmap[65] == 0 and for 'z', b64charmap[122] == 57
--
local b32charmap = {};
for i = 1, 32 do b32charmap[byte(b32chars, i)] = i - 1 end
local function decode(s)
-- reject invalid encoded strings
if s==string.sub(b32chars,1,1) then return('') end
if string.find(s, '[^'..b32chars..']') then
return nil, 'invalid char'
end
-- process leading zeros - count and remove them
local zn -- number of leading zeros (base32 digits '1')
-- zn = #(string.match(s, '^(1+)') or '')
-- s = string.gsub(s, '^(1+)', '')
-- special case: the string is empty or contains only null bytes
-- if s == '' then
-- return string.rep('\x00', zn)
-- end
--
-- process significant digits
local dn -- decoded number as an array of bytes (little-endian)
local d -- base32 digit, as an integer
local b -- a byte in dn
local m -- a byte multiplied by 32 (used for product)
local carry
dn = { b32charmap[byte(s, 1)] } --init with most significant digit
for i = 2, #s do --repeat until no more digits
-- multiply dn by 32, then add next digit
d = b32charmap[byte(s, i)] -- next digit
carry = 0
-- multiply dn by 32
for j = 1, #dn do
b = dn[j]
m = b * 32 + carry
b = m & 0xff
carry = m >> 8
dn[j] = b
end
if carry > 0 then dn[#dn + 1] = carry end
-- add next digit to dn
carry = d
for j = 1, #dn do
b = dn[j] + carry
carry = b >> 8
dn[j] = b & 0xff
end
if carry > 0 then dn[#dn + 1] = carry end
end
-- now dn contains the decoded number (little endian)
-- must add leading zeros and reverse dn to build binary string
local ben = {} -- big-endian number as array of chars
local ln = #dn
for i = 1, ln do
ben[i] = char(dn[ln-i+1])
end
return concat(ben)
-- return string.rep('\x00', zn) .. concat(ben)
end --decode()
return { -- base32 module
encode = encode,
decode = decode,
}
end
base32 = preload_1008b()
--*****************************************************************************
function preload_1008c()
local byte, char, concat = string.byte, string.char, table.concat
local b64chars = 'ABCDE'..'FGHJK'..'LMNPQ'..'RSTWX'..'YZ'..'0123456789'
local b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'..'abcdefghijklmnopqrstuvwxyz_-'
local function encode(s) -- encode byte array to 64 chars string
if #s==0 then return string.sub(b64chars,1,1) end
local q, b
local et = {}
local zn = 0 -- number of leading zero bytes in s
-- assume s is a large, little-endian binary number
-- with base256 digits (each byte is a 'digit')
local nt = {} -- number to divide in base 256, big endian
local dt = {} -- result of nt // 64, in base 256
local more = true -- used to count leading zero bytes
for i = 1, #s do
b = byte(s, i)
-- if more and b == 0 then
-- zn = zn + 1
-- else
-- more = false
-- end
nt[i] = b
end
-- if #s == zn then --take care of strings empty or with only nul bytes
-- return string.rep('1', zn)
-- end
more = true
while more do
local r = 0
more = false
for i = 1, #nt do
b = nt[i] + (256 * r)
q = b // 64
-- if q is not null at least once, we are good
-- for another division by 64
more = more or q > 0
r = b % 64
dt[i] = q
end
-- r is the next base64 digit. insert it before previous ones
-- to get a big-endian base64 number
table.insert(et, 1, char(byte(b64chars, r+1)))
-- now copy dt into nt before another round of division by 64
nt = {}
for i = 1, #dt do nt[i] = dt[i] end
dt = {}
end--while
-- don't forget the leading zeros ('1' is digit 0 in bitcoin base64 alphabet)
return concat(et)
-- return string.rep('1', zn) .. concat(et)
end --encode()
-- inverse base64 map, used by b64decode: b64charmap maps characters in
-- base64 alphabet to their _offset_ in b64chars (0-based, not 1-based...)
-- eg. for digit '1' b64charmap[65] == 0 and for 'z', b64charmap[122] == 57
--
local b64charmap = {};
for i = 1, 64 do b64charmap[byte(b64chars, i)] = i - 1 end
local function decode(s)
-- reject invalid encoded strings
if s==string.sub(b64chars,1,1) then return('') end
if string.find(s, '[^'..b64chars..']') then
return nil, 'invalid char'
end
-- process leading zeros - count and remove them
local zn -- number of leading zeros (base64 digits '1')
-- zn = #(string.match(s, '^(1+)') or '')
-- s = string.gsub(s, '^(1+)', '')
-- special case: the string is empty or contains only null bytes
-- if s == '' then
-- return string.rep('\x00', zn)
-- end
--
-- process significant digits
local dn -- decoded number as an array of bytes (little-endian)
local d -- base64 digit, as an integer
local b -- a byte in dn
local m -- a byte multiplied by 64 (used for product)
local carry
dn = { b64charmap[byte(s, 1)] } --init with most significant digit
for i = 2, #s do --repeat until no more digits
-- multiply dn by 64, then add next digit
d = b64charmap[byte(s, i)] -- next digit
carry = 0
-- multiply dn by 64
for j = 1, #dn do
b = dn[j]
m = b * 64 + carry
b = m & 0xff
carry = m >> 8
dn[j] = b
end
if carry > 0 then dn[#dn + 1] = carry end
-- add next digit to dn
carry = d
for j = 1, #dn do
b = dn[j] + carry
carry = b >> 8
dn[j] = b & 0xff
end
if carry > 0 then dn[#dn + 1] = carry end
end
-- now dn contains the decoded number (little endian)
-- must add leading zeros and reverse dn to build binary string
local ben = {} -- big-endian number as array of chars
local ln = #dn
for i = 1, ln do
ben[i] = char(dn[ln-i+1])
end
return concat(ben)
-- return string.rep('\x00', zn) .. concat(ben)
end --decode()
return { -- base64 module
encode = encode,
decode = decode,
}
end
base64 = preload_1008c()
--*****************************************************************************
function preload_1008d()
local byte, char, concat = string.byte, string.char, table.concat
local b62chars = 'ABCDE'..'FGHJK'..'LMNPQ'..'RSTWX'..'YZ'..'0123456789'
local b62chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'..'abcdefghijklmnopqrstuvwxyz'
local function encode(s) -- encode byte array to 62 chars string
if #s==0 then return string.sub(b62chars,1,1) end
local q, b
local et = {}
local zn = 0 -- number of leading zero bytes in s
-- assume s is a large, little-endian binary number
-- with base256 digits (each byte is a 'digit')
local nt = {} -- number to divide in base 256, big endian
local dt = {} -- result of nt // 62, in base 256
local more = true -- used to count leading zero bytes
for i = 1, #s do
b = byte(s, i)
-- if more and b == 0 then
-- zn = zn + 1
-- else
-- more = false
-- end
nt[i] = b
end
-- if #s == zn then --take care of strings empty or with only nul bytes
-- return string.rep('1', zn)
-- end
more = true
while more do
local r = 0
more = false
for i = 1, #nt do
b = nt[i] + (256 * r)
q = b // 62
-- if q is not null at least once, we are good
-- for another division by 62
more = more or q > 0
r = b % 62
dt[i] = q
end
-- r is the next base62 digit. insert it before previous ones
-- to get a big-endian base62 number
table.insert(et, 1, char(byte(b62chars, r+1)))
-- now copy dt into nt before another round of division by 62
nt = {}
for i = 1, #dt do nt[i] = dt[i] end
dt = {}
end--while
-- don't forget the leading zeros ('1' is digit 0 in bitcoin base62 alphabet)
return concat(et)
-- return string.rep('1', zn) .. concat(et)
end --encode()
-- inverse base62 map, used by b62decode: b62charmap maps characters in
-- base62 alphabet to their _offset_ in b62chars (0-based, not 1-based...)
-- eg. for digit '1' b62charmap[65] == 0 and for 'z', b62charmap[122] == 57
--
local b62charmap = {};
for i = 1, 62 do b62charmap[byte(b62chars, i)] = i - 1 end
local function decode(s)
-- reject invalid encoded strings
if s==string.sub(b62chars,1,1) then return('') end
if string.find(s, '[^'..b62chars..']') then
return nil, 'invalid char'
end
-- process leading zeros - count and remove them
local zn -- number of leading zeros (base62 digits '1')
-- zn = #(string.match(s, '^(1+)') or '')
-- s = string.gsub(s, '^(1+)', '')
-- special case: the string is empty or contains only null bytes
-- if s == '' then
-- return string.rep('\x00', zn)
-- end
--
-- process significant digits
local dn -- decoded number as an array of bytes (little-endian)
local d -- base62 digit, as an integer
local b -- a byte in dn
local m -- a byte multiplied by 62 (used for product)
local carry
dn = { b62charmap[byte(s, 1)] } --init with most significant digit
for i = 2, #s do --repeat until no more digits
-- multiply dn by 62, then add next digit
d = b62charmap[byte(s, i)] -- next digit
carry = 0
-- multiply dn by 62
for j = 1, #dn do
b = dn[j]
m = b * 62 + carry
b = m & 0xff
carry = m >> 8
dn[j] = b
end
if carry > 0 then dn[#dn + 1] = carry end
-- add next digit to dn
carry = d
for j = 1, #dn do
b = dn[j] + carry
carry = b >> 8
dn[j] = b & 0xff
end
if carry > 0 then dn[#dn + 1] = carry end
end
-- now dn contains the decoded number (little endian)
-- must add leading zeros and reverse dn to build binary string
local ben = {} -- big-endian number as array of chars
local ln = #dn
for i = 1, ln do
ben[i] = char(dn[ln-i+1])
end
return concat(ben)
-- return string.rep('\x00', zn) .. concat(ben)
end --decode()
local function encode(s) return(bin.stohex(s)) end
local function decode(s) return(bin.hextos(s)) end
return { -- base62 module
encode = encode,
decode = decode,
}
end
base62 = preload_1008d()
--*****************************************************************************
function preload_1001()
function preload_2002()
local app, concat = table.insert, table.concat
------------------------------------------------------------
-- salsa quarter round (rotl inlined)
local function qround(st,x,y,z,w)
-- st is a salsa state: an array of 16 u32 words
-- x,y,z,w are indices in st
local a, b, c, d = st[x], st[y], st[z], st[w]
local t
t = (a + d) & 0xffffffff
-- b = b ~ rotl32(t, 7)
b = b ~ ((t << 7) | (t >> 25)) & 0xffffffff
t = (b + a) & 0xffffffff
-- c = c ~ rotl32(t, 9)
c = c ~ ((t << 9) | (t >> 23)) & 0xffffffff
t = (c + b) & 0xffffffff
-- d = d ~ rotl32(t, 13)
d = d ~ ((t << 13) | (t >> 19)) & 0xffffffff
t = (d + c) & 0xffffffff
-- a = a ~ rotl32(t, 18)
a = a ~ ((t << 18) | (t >> 14)) & 0xffffffff
st[x], st[y], st[z], st[w] = a, b, c, d
return st
end
-- salsa20 state and working state are allocated once and reused
-- by each invocation of salsa20_block()
local salsa20_state = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
local salsa20_working_state = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
local salsa20_block = function(key, counter, nonce)
-- key: u32[8]
-- counter: u32[2]
-- nonce: u32[2]
local st = salsa20_state -- state
local wst = salsa20_working_state -- working state
-- initialize state
st[1], st[6], st[11], st[16] =
0x61707865, 0x3320646e, 0x79622d32, 0x6b206574
for i = 1, 4 do
st[i+1] = key[i]
st[i+11] = key[i+4]
end
st[7], st[8], st[9], st[10] = nonce[1], nonce[2], counter[1], counter[2]
-- copy state to working_state
for i = 1, 16 do wst[i] = st[i] end
-- run 20 rounds, ie. 10 iterations of 8 quarter rounds
for _ = 1, 10 do --RFC reference:
qround(wst, 1,5,9,13) --1. QUARTERROUND ( 0, 4, 8,12)
qround(wst, 6,10,14,2) --2. QUARTERROUND ( 5, 9,13, 1)
qround(wst, 11,15,3,7) --3. QUARTERROUND (10,14, 2, 6)
qround(wst, 16,4,8,12) --4. QUARTERROUND (15, 3, 7,11)
qround(wst, 1,2,3,4) --5. QUARTERROUND ( 0, 1, 2, 3)
qround(wst, 6,7,8,5) --6. QUARTERROUND ( 5, 6, 7, 4)
qround(wst, 11,12,9,10) --7. QUARTERROUND (10,11, 8, 9)
qround(wst, 16,13,14,15) --8. QUARTERROUND (15,12,13,14)
end
-- add working_state to state
for i = 1, 16 do st[i] = (st[i] + wst[i]) & 0xffffffff end
-- return st, an array of 16 u32 words used as a keystream
return st
end --salsa20_block()
local function hsalsa20_block(key, counter, nonce)
error2("salsa0001")
local st = salsa20_block(key, counter, nonce)
error2("salsa0002")
return {
(st[1] - 0x61707865) & 0xffffffff,
(st[6] - 0x3320646e) & 0xffffffff,
(st[11] - 0x79622d32) & 0xffffffff,
(st[16] - 0x6b206574) & 0xffffffff,
(st[7] - nonce[1]) & 0xffffffff,
(st[8] - nonce[2]) & 0xffffffff,
(st[9] - counter[1]) & 0xffffffff,
(st[10] - counter[2]) & 0xffffffff,
}
end
-- pat16: used to unpack a 64-byte string as 16 uint32 and vice versa
local pat16 = '<I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4'
-- pat8: used to unpack a 32-byte string as 8 uint32 and vice versa
local pat8 = '<I4I4I4I4I4I4I4I4'
local function salsa20_encrypt_block(key, counter, nonce, pt, ptidx)
-- encrypt a 64-byte block of plain text.
-- key: 32 bytes as an array of 8 uint32
-- counter: 8 bytes as an array of 2 uint32
-- nonce: 8 bytes as an array of 2 uint32
-- pt: plain text string,
-- ptidx: index of beginning of block in plain text (origin=1)
-- if less than 64 bytes are left at position ptidx, it is padded
-- with null bytes before encryption and result is stripped
-- accordingly.
-- return encrypted block as a string (length <= 16)
local rbn = #pt - ptidx + 1 -- number of remaining bytes in pt
if rbn < 64 then
local tmp = string.sub(pt, ptidx)
pt = tmp .. string.rep('\0', 64 - rbn) --pad last block
ptidx = 1
end
assert(#pt >= 64)
local ba = table.pack(string.unpack(pat16, pt, ptidx))
local keystream = salsa20_block(key, counter, nonce)
for i = 1, 16 do
ba[i] = ba[i] ~ keystream[i]
end
local es = string.pack(pat16, table.unpack(ba))
if rbn < 64 then
es = string.sub(es, 1, rbn)
end
return es
end --salsa20_encrypt_block
local salsa20_encrypt = function(key, counter, nonce, pt)
-- encrypt plain text 'pt', return encrypted text
-- key: 32 bytes as a string
-- counter: an uint64 (must be incremented for each block)
-- nonce: 8 bytes as a string
-- pt: plain text string
assert(#key == 32, '#key must be 32')
assert(#nonce == 8, '#nonce must be 8')
local keya = table.pack(string.unpack('<I4I4I4I4I4I4I4I4', key))
local noncea = table.pack(string.unpack('<I4I4', nonce))
local countera = {counter & 0xffffffff, counter >> 32}
local t = {} -- used to collect all encrypted blocks
local ptidx = 1
while ptidx <= #pt do
-- feed_the_dog()
app(t, salsa20_encrypt_block(keya, countera, noncea, pt, ptidx))
ptidx = ptidx + 64
countera[1] = countera[1] + 1
if countera[1] > 0xffffffff then
countera[1] = 0
countera[2] = countera[2] + 1
end
end
return (concat(t))
end --salsa20_encrypt()
local function salsa20_stream(key, counter, nonce, length)
assert(#key == 32, '#key must be 32')
assert(#nonce == 8, '#nonce must be 8')
local keya = table.pack(string.unpack('<I4I4I4I4I4I4I4I4', key))
local noncea = table.pack(string.unpack('<I4I4', nonce))
local countera = {counter & 0xffffffff, counter >> 32}
local t = {} -- used to collect all encrypted blocks
while length > 0 do
-- feed_the_dog()
local keystream = salsa20_block(keya, countera, noncea)
local block = string.pack(pat16, table.unpack(keystream))
if length <= 64 then block = block:sub(1, length) end
app(t, block)
length = length - 64
countera[1] = countera[1] + 1
if countera[1] > 0xffffffff then
countera[1] = 0
countera[2] = countera[2] + 1
end
end
return (concat(t))
end
local hsalsa20 = function(key, counter, nonce)
assert(#key == 32, '#key must be 32')
assert(#nonce == 8, '#nonce must be 8')
local keya = table.pack(string.unpack('<I4I4I4I4I4I4I4I4', key))
local noncea = table.pack(string.unpack('<I4I4', nonce))
local countera = {counter & 0xffffffff, counter >> 32}
local stream = hsalsa20_block(keya, countera, noncea)
return string.pack(pat8, table.unpack(stream))
end
------------------------------------------------------------
return {
encrypt = salsa20_encrypt,
decrypt = salsa20_encrypt,
stream = salsa20_stream,
hsalsa20 = hsalsa20,
--
key_size = 32,
nonce_size = 8,
}
--end of salsa20
end
salsa20 = preload_2002()
function preload_2003()
------------------------------------------------------------
-- set25519() not used
local function car25519(o)
local c
for i = 1, 16 do
o[i] = o[i] + 65536 -- 1 << 16
-- lua '>>' doesn't perform sign extension...
-- so the following >>16 doesn't work with negative numbers!!
-- ...took a bit of time to find this one :-)
-- c = o[i] >> 16
c = o[i] // 65536
if i < 16 then
o[i+1] = o[i+1] + (c - 1)
else
o[1] = o[1] + 38 * (c - 1)
end
o[i] = o[i] - (c << 16)
end
end --car25519()
local function sel25519(p, q, b)
local c = ~(b-1)
local t
for i = 1, 16 do
t = c & (p[i] ~ q[i])
p[i] = p[i] ~ t
q[i] = q[i] ~ t
end
end --sel25519
local function pack25519(o, n)
-- out o[32], in n[16]
local m, t = {}, {}
local b
for i = 1, 16 do t[i] = n[i] end
car25519(t)
car25519(t)
car25519(t)
for _ = 1, 2 do
m[1] = t[1] - 0xffed
for i = 2, 15 do
m[i] = t[i] - 0xffff - ((m[i-1] >> 16) & 1)
m[i-1] = m[i-1] & 0xffff
end
m[16] = t[16] - 0x7fff - ((m[15] >> 16) & 1)
b = (m[16] >> 16) & 1
m[15] = m[15] & 0xffff
sel25519(t, m, 1-b)
end
for i = 1, 16 do
o[2*i-1] = t[i] & 0xff
o[2*i] = t[i] >> 8
end
end -- pack25519
-- neq25519() not used
-- par25519() not used
local function unpack25519(o, n)
-- out o[16], in n[32]
for i = 1, 16 do
o[i] = n[2*i-1] + (n[2*i] << 8)
end
o[16] = o[16] & 0x7fff
end -- unpack25519
local function A(o, a, b) --add
for i = 1, 16 do o[i] = a[i] + b[i] end
end
local function Z(o, a, b) --sub
for i = 1, 16 do o[i] = a[i] - b[i] end
end
local function M(o, a, b) --mul gf, gf -> gf
local t = {}
for i = 1, 32 do t[i] = 0 end
for i = 1, 16 do
for j = 1, 16 do
t[i+j-1] = t[i+j-1] + (a[i] * b[j])
end
end
for i = 1, 15 do t[i] = t[i] + 38 * t[i+16] end
for i = 1, 16 do o[i] = t[i] end
car25519(o)
car25519(o)
end
local function S(o, a) --square
M(o, a, a)
end
local function inv25519(o, i)
local c = {}
for a = 1, 16 do c[a] = i[a] end
for a = 253, 0, -1 do
S(c, c)
if a ~= 2 and a ~= 4 then M(c, c, i) end
end
for a = 1, 16 do o[a] = c[a] end
--~ pt(o)
end
--pow2523() not used
local t_121665 = {0xDB41,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
local function crypto_scalarmult(q, n, p)
-- out q[], in n[], in p[]
local z = {}
local x = {}
local a = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
local b = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
local c = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
local d = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
local e = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
local f = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
for i = 1, 31 do z[i] = n[i] end
z[32] = (n[32] & 127) | 64
z[1] = z[1] & 248
--~ pt(z)
unpack25519(x, p)
--~ pt(x)
for i = 1, 16 do
b[i] = x[i]
a[i] = 0
c[i] = 0
d[i] = 0
end
a[1] = 1
d[1] = 1
for i = 254, 0, -1 do -- urspruenglich 254
if i%10 == 0 and file_open ~= nil then
if file_open ~= nil then
start_timer(26,WAIT_26,0,1)
-- error2("crypto_scalarmult_0001 "..tostring(i))
coroutine.yield()
else
notify("Crypto, WAIT26")
end
end
local r = (z[(i>>3)+1] >> (i & 7)) & 1
sel25519(a,b,r)
sel25519(c,d,r)
A(e,a,c)
Z(a,a,c)
A(c,b,d)
Z(b,b,d)
S(d,e)
S(f,a)
M(a,c,a)
M(c,b,e)
A(e,a,c)
Z(a,a,c)
-- error2("crypto_scalarmult_0155 "..tostring(i))
S(b,a)
Z(c,d,f)
M(a,c,t_121665)
A(a,a,d)
M(c,c,a)
M(a,d,f)
M(d,b,x)
S(b,e)
-- error2("crypto_scalarmult_0156 "..tostring(i))
sel25519(a,b,r)
sel25519(c,d,r)
end
error2("crypto_scalarmult_0002")
for i = 1, 16 do
x[i+16] = a[i]
x[i+32] = c[i]
x[i+48] = b[i]
x[i+64] = d[i]
end
-- cannot use pointer arithmetics...
local x16, x32 = {}, {}
for i = 1, #x do
if i > 16 then x16[i-16] = x[i] end
if i > 32 then x32[i-32] = x[i] end
end
error2("crypto_scalarmult_0003")
inv25519(x32,x32)
error2("crypto_scalarmult_0004")
M(x16,x16,x32)
error2("crypto_scalarmult_0005")
pack25519(q,x16)
error2("crypto_scalarmult_0006")
if file_open ~= nil then
start_timer(26,WAIT_26,0,1)
error2("crypto_scalarmult_0009 ")
coroutine.yield()
else
notify("Crypto, WAIT26")
end
return 0
end -- crypto_scalarmult
local t_9 = { -- u8 * 32
9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
}
local function crypto_scalarmult_base(q, n)
-- out q[], in n[]
return crypto_scalarmult(q, n, t_9)
end
------------------------------------------------------------------------
-- convenience function (using binary strings instead of byte tables)
--
-- curve point and scalars are represented as 32-byte binary strings
-- (encoded as little endian)
local function scalarmult(n, p)
-- n, a scalar (little endian) as a 32-byte string
-- p, a curve point as a 32-byte string
-- return the scalar product np as a 32-byte string
local qt, nt, pt = {}, {}, {}
for i = 1, 32 do
nt[i] = string.byte(n, i)
pt[i] = string.byte(p, i)
end
crypto_scalarmult(qt, nt, pt)
local q = string.char(table.unpack(qt))
return q
end
-- base: the curve point generator = 9
local base = '\9' .. ('\0'):rep(31)
return {
crypto_scalarmult = crypto_scalarmult,
crypto_scalarmult_base = crypto_scalarmult_base,
--
-- convenience function and definition
--
scalarmult = scalarmult,
base = base,
--
}
-- end of ec25519 module
end
ec25519 = preload_2003()
function preload_2004()
-----------------------------------------------------------
-- poly1305
local sunp = string.unpack
local function poly_init(k)
-- k: 32-byte key as a string
-- initialize internal state
local st = {
r = {
(sunp('<I4', k, 1) ) & 0x3ffffff, --r0
(sunp('<I4', k, 4) >> 2) & 0x3ffff03, --r1
(sunp('<I4', k, 7) >> 4) & 0x3ffc0ff, --r2
(sunp('<I4', k, 10) >> 6) & 0x3f03fff, --r3
(sunp('<I4', k, 13) >> 8) & 0x00fffff, --r4
},
h = { 0,0,0,0,0 },
pad = { sunp('<I4', k, 17), -- 's' in rfc
sunp('<I4', k, 21),
sunp('<I4', k, 25),
sunp('<I4', k, 29),
},
buffer = '', --
leftover = 0,
final = false,
}--st
return st
end --poly_init()
local function poly_blocks(st, m)
-- st: internal state
-- m: message:string
local bytes = #m
local midx = 1
local hibit = st.final and 0 or 0x01000000 -- 1 << 24
local r0 = st.r[1]
local r1 = st.r[2]
local r2 = st.r[3]
local r3 = st.r[4]
local r4 = st.r[5]
local s1 = r1 * 5
local s2 = r2 * 5
local s3 = r3 * 5
local s4 = r4 * 5
local h0 = st.h[1]
local h1 = st.h[2]
local h2 = st.h[3]
local h3 = st.h[4]
local h4 = st.h[5]
local d0, d1, d2, d3, d4, c
--
while bytes >= 16 do -- 16 = poly1305_block_size
-- h += m[i] (in rfc: a += n with 0x01 byte)
h0 = h0 + ((sunp('<I4', m, midx ) ) & 0x3ffffff)
h1 = h1 + ((sunp('<I4', m, midx + 3) >> 2) & 0x3ffffff)
h2 = h2 + ((sunp('<I4', m, midx + 6) >> 4) & 0x3ffffff)
h3 = h3 + ((sunp('<I4', m, midx + 9) >> 6) & 0x3ffffff)
h4 = h4 + ((sunp('<I4', m, midx + 12) >> 8) | hibit)--0x01 byte
--
-- h *= r % p (partial)
d0 = h0*r0 + h1*s4 + h2*s3 + h3*s2 + h4*s1
d1 = h0*r1 + h1*r0 + h2*s4 + h3*s3 + h4*s2
d2 = h0*r2 + h1*r1 + h2*r0 + h3*s4 + h4*s3
d3 = h0*r3 + h1*r2 + h2*r1 + h3*r0 + h4*s4
d4 = h0*r4 + h1*r3 + h2*r2 + h3*r1 + h4*r0
--
c = (d0>>26) & 0xffffffff ; h0 = d0 & 0x3ffffff
d1 = d1 + c ; c = (d1>>26) & 0xffffffff ; h1 = d1 & 0x3ffffff
d2 = d2 + c ; c = (d2>>26) & 0xffffffff ; h2 = d2 & 0x3ffffff
d3 = d3 + c ; c = (d3>>26) & 0xffffffff ; h3 = d3 & 0x3ffffff
d4 = d4 + c ; c = (d4>>26) & 0xffffffff ; h4 = d4 & 0x3ffffff
h0 = h0 + (c*5) ; c = h0>>26 ; h0 = h0 & 0x3ffffff
h1 = h1 + c
--
midx = midx + 16 -- 16 = poly1305_block_size
bytes = bytes - 16
end --while
st.h[1] = h0
st.h[2] = h1
st.h[3] = h2
st.h[4] = h3
st.h[5] = h4
st.bytes = bytes -- remaining bytes. must be < 16 here
st.midx = midx -- index of first remaining bytes
return st
end --poly_blocks()
local function poly_update(st, m)
-- st: internal state
-- m: message:string
st.bytes, st.midx = #m, 1
-- process full blocks if any
if st.bytes >= 16 then
poly_blocks(st, m)
end
--handle remaining bytes
if st.bytes == 0 then -- no bytes left
-- nothing to do? no add 0x01? - apparently not.
else
local buffer = string.sub(m, st.midx)
.. '\x01' .. string.rep('\0', 16 - st.bytes -1)
assert(#buffer == 16)
st.final = true -- this is the last block
--~ p16(buffer)
poly_blocks(st, buffer)
end
--
return st
end --poly_update
local function poly_finish(st)
--
local c, mask --u32
local f --u64
-- fully carry h
local h0 = st.h[1]
local h1 = st.h[2]
local h2 = st.h[3]
local h3 = st.h[4]
local h4 = st.h[5]
--
c = h1 >> 26; h1 = h1 & 0x3ffffff
h2 = h2 + c; c = h2 >> 26; h2 = h2 & 0x3ffffff
h3 = h3 + c; c = h3 >> 26; h3 = h3 & 0x3ffffff
h4 = h4 + c; c = h4 >> 26; h4 = h4 & 0x3ffffff
h0 = h0 + (c*5); c = h0 >> 26; h0 = h0 & 0x3ffffff
h1 = h1 + c
--
--compute h + -p
local g0 = (h0 + 5) ; c = g0 >> 26; g0 = g0 & 0x3ffffff
local g1 = (h1 + c) ; c = g1 >> 26; g1 = g1 & 0x3ffffff
local g2 = (h2 + c) ; c = g2 >> 26; g2 = g2 & 0x3ffffff
local g3 = (h3 + c) ; c = g3 >> 26; g3 = g3 & 0x3ffffff
local g4 = (h4 + c - 0x4000000) &0xffffffff -- (1 << 26)
--
-- select h if h < p, or h + -p if h >= p
mask = ((g4 >> 31) -1) & 0xffffffff
--
g0 = g0 & mask
g1 = g1 & mask
g2 = g2 & mask
g3 = g3 & mask
g4 = g4 & mask
--
mask = (~mask) & 0xffffffff
h0 = (h0 & mask) | g0
h1 = (h1 & mask) | g1
h2 = (h2 & mask) | g2
h3 = (h3 & mask) | g3
h4 = (h4 & mask) | g4
--
--h = h % (2^128)
h0 = ((h0 ) | (h1 << 26)) & 0xffffffff
h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff
h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff
h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff
--
-- mac = (h + pad) % (2^128)
f = h0 + st.pad[1] ; h0 = f & 0xffffffff
f = h1 + st.pad[2] + (f >> 32) ; h1 = f & 0xffffffff
f = h2 + st.pad[3] + (f >> 32) ; h2 = f & 0xffffffff
f = h3 + st.pad[4] + (f >> 32) ; h3 = f & 0xffffffff
--
local mac = string.pack('<I4I4I4I4', h0, h1, h2, h3)
-- (should zero out the state?)
--
return mac
end --poly_finish()
local function poly_auth(m, k)
-- m: msg string
-- k: key string (must be 32 bytes)
-- return mac 16-byte string
assert(#k == 32)
local st = poly_init(k)
poly_update(st, m)
local mac = poly_finish(st)
return mac
end --poly_auth()
local function poly_verify(m, k, mac)
local macm = poly_auth(m, k)
return macm == mac
end --poly_verify()
------------------------------------------------------------
-- return poly1305 module
return {
init = poly_init,
update = poly_update,
finish = poly_finish,
auth = poly_auth,
verify = poly_verify,
}
end
local poly1305 = preload_2004()
local function public_key(sk)
assert(type(sk) == 'string', 'sk must be a string')
assert(#sk == 32, '#sh must be 32')
sk = table.pack(sk:byte(1, 32))
local pk = {}
ec25519.crypto_scalarmult_base(pk, sk)
return string.char(table.unpack(pk))
end
local function unpack_nonce(nonce)
assert(#nonce == 24, '#nonce must be 24')
local nonce1 = nonce:sub(1, 8)
local counter = string.unpack('<I8', nonce:sub(9,16))
local nonce2 = nonce:sub(17,24)
return counter, nonce1, nonce2
end
local function secretbox(pt, nonce, key)
assert(#key == 32, '#key must be 32')
local counter, nonce1, nonce2 = unpack_nonce(nonce)
local key2 = salsa20.hsalsa20(key, counter, nonce1)
local et = salsa20.encrypt(key2, 0, nonce2, string.rep('\0', 32) .. pt)
local key3 = et:sub(1, 32)
et = et:sub(33)
local mac = poly1305.auth(et, key3)
return mac .. et
end
local function secretbox_open(et, nonce, key)
assert(#key == 32, '#key must be 32')
assert(#et >= 16, '#et must be at least 16')
local counter, nonce1, nonce2 = unpack_nonce(nonce)
local key2 = salsa20.hsalsa20(key, counter, nonce1)
local key3 = salsa20.stream(key2, 0, nonce2, 32)
local mac = et:sub(1, 16)
local mac2 = poly1305.auth(et:sub(17), key3)
if mac2 ~= mac then return nil, 'invalid MAC' end
local pt = salsa20.encrypt(key2, 0, nonce2, string.rep('\0', 16) .. et)
return pt:sub(33)
end
local function stream_key(pk, sk)
assert(#pk == 32, '#pk must be 32')
assert(#sk == 32, '#pk must be 32')
pk = table.pack(pk:byte(1, 32))
sk = table.pack(sk:byte(1, 32))
local k = {}
ec25519.crypto_scalarmult(k, sk, pk)
k = string.char(table.unpack(k))
return salsa20.hsalsa20(k, 0, string.rep('\0', 8))
end
local function box(pt, nonce, pk_b, sk_a)
return secretbox(pt, nonce, stream_key(pk_b, sk_a))
end
local function box_open(et, nonce, pk_a, sk_b)
return secretbox_open(et, nonce, stream_key(pk_a, sk_b))
end
return {
public_key = public_key,
secretbox = secretbox,
secretbox_open = secretbox_open,
stream_key = stream_key,
box = box,
box_open = box_open,
}
end
crypto = preload_1001()
--********************************************************************************
function preload_1009()
-- some local definitions
local strf = string.format
local byte, char = string.byte, string.char
local spack, sunpack = string.pack, string.unpack
local app, concat = table.insert, table.concat
local transform_table = {
['0'] = '0000',
['1'] = '0001',
['2'] = '0010',
['3'] = '0011',
['4'] = '0100',
['5'] = '0101',
['6'] = '0110',
['7'] = '0111',
['8'] = '1000',
['9'] = '1001',
['a'] = '1010',
['b'] = '1011',
['c'] = '1100',
['d'] = '1101',
['e'] = '1110',
['f'] = '1111',
['0000'] = '0',
['0001'] = '1',
['0010'] = '2',
['0011'] = '3',
['0100'] = '4',
['0101'] = '5',
['0110'] = '6',
['0111'] = '7',
['1000'] = '8',
['1001'] = '9',
['1010'] = 'a',
['1011'] = 'b',
['1100'] = 'c',
['1101'] = 'd',
['1110'] = 'e',
['1111'] = 'f' }
------------------------------------------------------------------------
local function bintohex (h)
local text = {}
h = table.concat(h)
while h ~= '' do
h1 = string.sub(h,1,4)
h = string.sub(h,5)
table.insert(text,transform_table[h1])
end
return table.concat(text)
end
local function bintohex1 (h)
local text = {}
while h ~= '' do
h1 = string.sub(h,1,4)
h = string.sub(h,5)
table.insert(text,transform_table[h1])
end
return table.concat(text)
end
------------------------------------------------------------------------
local function hextobin (h)
local text = {}
while h ~= '' do
h1 = string.sub(h,1,1)
h = string.sub(h,2)
local x = transform_table[h1]
table.insert(text,string.sub(x,1,1))
table.insert(text,string.sub(x,2,2))
table.insert(text,string.sub(x,3,3))
table.insert(text,string.sub(x,4,4))
end
return text
end
------------------------------------------------------------------------
local function stohex(s, ln, sep)
-- stohex(s [, ln [, sep)
-- return the hex encoding of string s
-- ln: (optional) a newline is inserted after 'ln' bytes
-- ie. after 2*ln hex digits. Defaults to no newlines.
-- sep: (optional) separator between bytes in the encoded string
-- defaults to nothing (if ln is nil, sep is ignored)
-- example:
-- stohex('abcdef', 4, ':') => '61:62:63:64\n65:66'
-- stohex('abcdef') => '616263646566'
--
if s == nil or #s == 0 then return '' end
if not ln then -- no newline, no separator: do it the fast way!
return (s:gsub('.',
function(c) return strf('%02x', byte(c)) end
))
end
sep = sep or '' -- optional separator between each byte
local t = {}
for i = 1, #s - 1 do
t[#t + 1] = strf('%02x%s', s:byte(i),
(i % ln == 0) and '\n' or sep)
end
-- last byte, without any sep appended
t[#t + 1] = strf('%02x', s:byte(#s))
return concat(t)
end --stohex()
local function hextos(hs, unsafe)
if #hs%2==1 then hs = "0" .. hs end
-- while string.sub(hs,1,2) == "00" do
-- hs = string.sub(hs,3)
-- end
-- decode an hex encoded string. return the decoded string
-- if optional parameter unsafe is defined, assume the hex
-- string is well formed (no checks, no whitespace removal).
-- Default is to remove white spaces (incl newlines)
-- and check that the hex string is well formed
-- local tonumber = tonumber
-- local function tonumber1 (c, base) -- Fehler in der urpsruenglichen LUA-Version auf dem Geraet
-- -- diese Funktion ist jetzt ersetzt durch die Funktion tonumber16
-- local nr = 0
-- while 0 == 0 do
-- nr = nr * 16
-- local c1 = string.sub(c,1,1)
-- if c1 == 'a' or c1 == 'A' then nr = nr + 10
-- elseif c1 == 'b' or c1 == 'B' then nr = nr + 11
-- elseif c1 == 'c' or c1 == 'C' then nr = nr + 12
-- elseif c1 == 'd' or c1 == 'D' then nr = nr + 13
-- elseif c1 == 'e' or c1 == 'E' then nr = nr + 14
-- elseif c1 == 'f' or c1 == 'F' then nr = nr + 15
-- else nr = nr + tonumber(c1) end
-- if #c == 1 then
-- break
-- end
-- c = string.sub(c,2)
-- end
-- return nr
-- end
-- if not unsafe then
-- hs = string.gsub(hs, '%s+', '') -- remove whitespaces
-- if string.find(hs, '[^0-9A-Za-z]') or #hs % 2 ~= 0 then
---- error('invalid hex string')
-- return('')
-- end
-- end
return hs:gsub('(%x%x)', function(c)
return char( tonumber16(c, 16))
end)
-- function(c) return string.char(tonumber(c, 16)) end
-- )
end -- hextos
local function rotr32(i, n)
-- rotate right on 32 bits
return ((i >> n) | (i << (32 - n))) & 0xffffffff
end
local function rotl32(i, n)
-- rotate left on 32 bits
return ((i << n) | (i >> (32 - n))) & 0xffffffff
end
local function xor1(key, plain)
-- return a string which is a xor of plain and key
-- plain and key may have arbitrary length.
-- the result has the same length as plain.
-- naive implementation, one byte at a time (ok for small strings)
local ot = {}
local ki, kln = 1, #key
for i = 1, #plain do
ot[#ot + 1] = char(byte(plain, i) ~ byte(key, ki))
ki = ki + 1
if ki > kln then ki = 1 end
end
return concat(ot)
end --xor1
local function xor8(key, plain)
-- return a string which is a xor of plain and key
-- plain may have arbitrary length.
-- ** key length (in bytes) must be a multiple of 8 **
-- the result has the same length as plain.
-- (result is computed one uint64 at a time)
assert(#key % 8 == 0, 'key not a multiple of 8 bytes')
local ka = {} -- key as an array of uint64
for i = 1, #key, 8 do
-- !!beware below: () around sunpack are needed:
-- app aka table.insert takes optionally 3 args
-- and sunpack returns 2 args...
app(ka, (sunpack('<I8', key, i)))
end
local kaln = #ka
local rbn = #plain -- remaining bytes in plain
local kai = 1 -- index in ka
local ot = {} -- table to collect output
local ibu -- an input block, as a uint64
local ob -- an output block as a string
for i = 1, #plain, 8 do
if rbn < 8 then
local buffer = string.sub(plain, i) .. string.rep('\0', 8 - rbn)
ibu = sunpack('<I8', buffer)
ob = string.sub(spack('<I8', ibu ~ ka[kai]), 1, rbn)
else
ibu = sunpack('<I8', plain, i)
ob = spack('<I8', ibu ~ ka[kai])
rbn = rbn - 8
kai = (kai < kaln) and (kai + 1) or 1
end
app(ot, ob)
end
return concat(ot)
end --xor8
------------------------------------------------------------------------
return { -- bin module
stohex = stohex,
hextos = hextos,
--
rotr32 = rotr32,
rotl32 = rotl32,
--
xor1 = xor1,
xor8 = xor8,
--
hextobin = hextobin,
bintohex = bintohex,
bintohex1 = bintohex1,
}
end
bin = preload_1009()
--*******************************************************************************
function tonumber16 (c, base) -- replacement for tonumber(...,16)
local nr = 0
while 0 == 0 do
nr = nr * 16
local c1 = string.sub(c,1,1)
if c1 == 'a' or c1 == 'A' then nr = nr + 10
elseif c1 == 'b' or c1 == 'B' then nr = nr + 11
elseif c1 == 'c' or c1 == 'C' then nr = nr + 12
elseif c1 == 'd' or c1 == 'D' then nr = nr + 13
elseif c1 == 'e' or c1 == 'E' then nr = nr + 14
elseif c1 == 'f' or c1 == 'F' then nr = nr + 15
else nr = nr + tonumber(c1) end
if #c == 1 then
break
end
c = string.sub(c,2)
end
return nr
end
--*******************************************************************************
function tonumber2 (c, base) -- replacement for tonumber(...,2)
local nr = 0
while 0 == 0 do
nr = nr * 2
local c1 = string.sub(c,1,1)
nr = nr + tonumber(c1)
if #c == 1 then
break
end
c = string.sub(c,2)
end
return nr
end
--***************************************************************************************
---
-- An implementation of the lua 5.2 bit32 library, in pure Lua
-- Note that in Lua, "x % n" is defined such that will always return a number
-- between 0 and n-1 for positive n. We take advantage of that a lot here.
function checkint( name, argidx, x, level )
local n = tonumber( x )
if not n then
error( string.format(
"bad argument #%d to '%s' (number expected, got %s)",
argidx, name, type( x )
), level + 1 )
end
return math.floor( n )
end
function checkint32( name, argidx, x, level )
local n = tonumber( x )
if not n then
error( string.format(
"bad argument #%d to '%s' (number expected, got %s)",
argidx, name, type( x )
), level + 1 )
end
return math.floor( n ) % 0x100000000
end
function bit32_bnot( x )
x = checkint32( 'bnot', 1, x, 2 )
-- In two's complement, -x = not(x) + 1
-- So not(x) = -x - 1
return ( -x - 1 ) % 0x100000000
end
---
-- Logic tables for and/or/xor. We do pairs of bits here as a tradeoff between
-- table space and speed. If you change the number of bits, also change the
-- constants 2 and 4 in comb() below, and the initial value in bit32_band and
-- bit32_btest
logic_and = {
[0] = { [0] = 0, 0, 0, 0},
[1] = { [0] = 0, 1, 0, 1},
[2] = { [0] = 0, 0, 2, 2},
[3] = { [0] = 0, 1, 2, 3},
}
logic_or = {
[0] = { [0] = 0, 1, 2, 3},
[1] = { [0] = 1, 1, 3, 3},
[2] = { [0] = 2, 3, 2, 3},
[3] = { [0] = 3, 3, 3, 3},
}
logic_xor = {
[0] = { [0] = 0, 1, 2, 3},
[1] = { [0] = 1, 0, 3, 2},
[2] = { [0] = 2, 3, 0, 1},
[3] = { [0] = 3, 2, 1, 0},
}
---
-- @param name string Function name
-- @param args table Function args
-- @param nargs number Arg count
-- @param s number Start value, 0-3
-- @param t table Logic table
-- @return number result
function comb( name, args, nargs, s, t )
for i = 1, nargs do
args[i] = checkint32( name, i, args[i], 3 )
end
local pow = 1
local ret = 0
for b = 0, 31, 2 do
local c = s
for i = 1, nargs do
c = t[c][args[i] % 4]
args[i] = math.floor( args[i] / 4 )
end
ret = ret + c * pow
pow = pow * 4
end
return ret
end
function bit32_band( ... )
return comb( 'band', { ... }, select( '#', ... ), 3, logic_and )
end
function bit32_bor( ... )
return comb( 'bor', { ... }, select( '#', ... ), 0, logic_or )
end
function bit32_bxor( ... )
return comb( 'bxor', { ... }, select( '#', ... ), 0, logic_xor )
end
function bit32_btest( ... )
return comb( 'btest', { ... }, select( '#', ... ), 3, logic_and ) ~= 0
end
function bit32_extract( n, field, width )
n = checkint32( 'extract', 1, n, 2 )
field = checkint( 'extract', 2, field, 2 )
width = checkint( 'extract', 3, width or 1, 2 )
if field < 0 then
error( "bad argument #2 to 'extract' (field cannot be negative)", 2 )
end
if width <= 0 then
error( "bad argument #3 to 'extract' (width must be positive)", 2 )
end
if field + width > 32 then
error( 'trying to access non-existent bits', 2 )
end
return math.floor( n / 2^field ) % 2^width
end
function bit32_replace( n, v, field, width )
n = checkint32( 'replace', 1, n, 2 )
v = checkint32( 'replace', 2, v, 2 )
field = checkint( 'replace', 3, field, 2 )
width = checkint( 'replace', 4, width or 1, 2 )
if field < 0 then
error( "bad argument #3 to 'replace' (field cannot be negative)", 2 )
end
if width <= 0 then
error( "bad argument #4 to 'replace' (width must be positive)", 2 )
end
if field + width > 32 then
error( 'trying to access non-existent bits', 2 )
end
local f = 2^field
local w = 2^width
local fw = f * w
return ( n % f ) + ( v % w ) * f + math.floor( n / fw ) * fw
end
-- For the shifting functions, anything over 32 is the same as 32
-- and limiting to 32 prevents overflow/underflow
function checkdisp( name, x )
x = checkint( name, 2, x, 3 )
return math.min( math.max( -32, x ), 32 )
end
function bit32_lshift( x, disp )
x = checkint32( 'lshift', 1, x, 2 )
disp = checkdisp( 'lshift', disp )
return math.floor( x * 2^disp ) % 0x100000000
end
function bit32_rshift( x, disp )
x = checkint32( 'rshift', 1, x, 2 )
disp = checkdisp( 'rshift', disp )
return math.floor( x / 2^disp ) % 0x100000000
end
function bit32_arshift( x, disp )
x = checkint32( 'arshift', 1, x, 2 )
disp = checkdisp( 'arshift', disp )
if disp <= 0 then
-- Non-positive displacement == left shift
-- (since exponent is non-negative, the multipication can never result
-- in a fractional part)
return ( x * 2^-disp ) % 0x100000000
elseif x < 0x80000000 then
-- High bit is 0 == right shift
-- (since exponent is positive, the division will never increase x)
return math.floor( x / 2^disp )
elseif disp > 31 then
-- Shifting off all bits
return 0xffffffff
else
-- 0x100000000 - 2 ^ ( 32 - disp ) creates a number with the high disp
-- bits set. So shift right then add that number.
return math.floor( x / 2^disp ) + ( 0x100000000 - 2 ^ ( 32 - disp ) )
end
end
-- For the rotation functions, disp works mod 32.
-- Note that lrotate( x, disp ) == rrotate( x, -disp ).
function bit32_lrotate( x, disp )
x = checkint32( 'lrotate', 1, x, 2 )
disp = checkint( 'lrotate', 2, disp, 2 ) % 32
local x = x * 2^disp
return ( x % 0x100000000 ) + math.floor( x / 0x100000000 )
end
function bit32_rrotate( x, disp )
x = checkint32( 'rrotate', 1, x, 2 )
disp = -checkint( 'rrotate', 2, disp, 2 ) % 32
local x = x * 2^disp
return ( x % 0x100000000 ) + math.floor( x / 0x100000000 )
end
--*******************************************************************************
function preload_1015()
local cclxvi = {[0] = {0,0,0,0,0,0,0,0}, {1,0,0,0,0,0,0,0}, {0,1,0,0,0,0,0,0}, {1,1,0,0,0,0,0,0},
{0,0,1,0,0,0,0,0}, {1,0,1,0,0,0,0,0}, {0,1,1,0,0,0,0,0}, {1,1,1,0,0,0,0,0},
{0,0,0,1,0,0,0,0}, {1,0,0,1,0,0,0,0}, {0,1,0,1,0,0,0,0}, {1,1,0,1,0,0,0,0},
{0,0,1,1,0,0,0,0}, {1,0,1,1,0,0,0,0}, {0,1,1,1,0,0,0,0}, {1,1,1,1,0,0,0,0},
{0,0,0,0,1,0,0,0}, {1,0,0,0,1,0,0,0}, {0,1,0,0,1,0,0,0}, {1,1,0,0,1,0,0,0},
{0,0,1,0,1,0,0,0}, {1,0,1,0,1,0,0,0}, {0,1,1,0,1,0,0,0}, {1,1,1,0,1,0,0,0},
{0,0,0,1,1,0,0,0}, {1,0,0,1,1,0,0,0}, {0,1,0,1,1,0,0,0}, {1,1,0,1,1,0,0,0},
{0,0,1,1,1,0,0,0}, {1,0,1,1,1,0,0,0}, {0,1,1,1,1,0,0,0}, {1,1,1,1,1,0,0,0},
{0,0,0,0,0,1,0,0}, {1,0,0,0,0,1,0,0}, {0,1,0,0,0,1,0,0}, {1,1,0,0,0,1,0,0},
{0,0,1,0,0,1,0,0}, {1,0,1,0,0,1,0,0}, {0,1,1,0,0,1,0,0}, {1,1,1,0,0,1,0,0},
{0,0,0,1,0,1,0,0}, {1,0,0,1,0,1,0,0}, {0,1,0,1,0,1,0,0}, {1,1,0,1,0,1,0,0},
{0,0,1,1,0,1,0,0}, {1,0,1,1,0,1,0,0}, {0,1,1,1,0,1,0,0}, {1,1,1,1,0,1,0,0},
{0,0,0,0,1,1,0,0}, {1,0,0,0,1,1,0,0}, {0,1,0,0,1,1,0,0}, {1,1,0,0,1,1,0,0},
{0,0,1,0,1,1,0,0}, {1,0,1,0,1,1,0,0}, {0,1,1,0,1,1,0,0}, {1,1,1,0,1,1,0,0},
{0,0,0,1,1,1,0,0}, {1,0,0,1,1,1,0,0}, {0,1,0,1,1,1,0,0}, {1,1,0,1,1,1,0,0},
{0,0,1,1,1,1,0,0}, {1,0,1,1,1,1,0,0}, {0,1,1,1,1,1,0,0}, {1,1,1,1,1,1,0,0},
{0,0,0,0,0,0,1,0}, {1,0,0,0,0,0,1,0}, {0,1,0,0,0,0,1,0}, {1,1,0,0,0,0,1,0},
{0,0,1,0,0,0,1,0}, {1,0,1,0,0,0,1,0}, {0,1,1,0,0,0,1,0}, {1,1,1,0,0,0,1,0},
{0,0,0,1,0,0,1,0}, {1,0,0,1,0,0,1,0}, {0,1,0,1,0,0,1,0}, {1,1,0,1,0,0,1,0},
{0,0,1,1,0,0,1,0}, {1,0,1,1,0,0,1,0}, {0,1,1,1,0,0,1,0}, {1,1,1,1,0,0,1,0},
{0,0,0,0,1,0,1,0}, {1,0,0,0,1,0,1,0}, {0,1,0,0,1,0,1,0}, {1,1,0,0,1,0,1,0},
{0,0,1,0,1,0,1,0}, {1,0,1,0,1,0,1,0}, {0,1,1,0,1,0,1,0}, {1,1,1,0,1,0,1,0},
{0,0,0,1,1,0,1,0}, {1,0,0,1,1,0,1,0}, {0,1,0,1,1,0,1,0}, {1,1,0,1,1,0,1,0},
{0,0,1,1,1,0,1,0}, {1,0,1,1,1,0,1,0}, {0,1,1,1,1,0,1,0}, {1,1,1,1,1,0,1,0},
{0,0,0,0,0,1,1,0}, {1,0,0,0,0,1,1,0}, {0,1,0,0,0,1,1,0}, {1,1,0,0,0,1,1,0},
{0,0,1,0,0,1,1,0}, {1,0,1,0,0,1,1,0}, {0,1,1,0,0,1,1,0}, {1,1,1,0,0,1,1,0},
{0,0,0,1,0,1,1,0}, {1,0,0,1,0,1,1,0}, {0,1,0,1,0,1,1,0}, {1,1,0,1,0,1,1,0},
{0,0,1,1,0,1,1,0}, {1,0,1,1,0,1,1,0}, {0,1,1,1,0,1,1,0}, {1,1,1,1,0,1,1,0},
{0,0,0,0,1,1,1,0}, {1,0,0,0,1,1,1,0}, {0,1,0,0,1,1,1,0}, {1,1,0,0,1,1,1,0},
{0,0,1,0,1,1,1,0}, {1,0,1,0,1,1,1,0}, {0,1,1,0,1,1,1,0}, {1,1,1,0,1,1,1,0},
{0,0,0,1,1,1,1,0}, {1,0,0,1,1,1,1,0}, {0,1,0,1,1,1,1,0}, {1,1,0,1,1,1,1,0},
{0,0,1,1,1,1,1,0}, {1,0,1,1,1,1,1,0}, {0,1,1,1,1,1,1,0}, {1,1,1,1,1,1,1,0},
{0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,1}, {0,1,0,0,0,0,0,1}, {1,1,0,0,0,0,0,1},
{0,0,1,0,0,0,0,1}, {1,0,1,0,0,0,0,1}, {0,1,1,0,0,0,0,1}, {1,1,1,0,0,0,0,1},
{0,0,0,1,0,0,0,1}, {1,0,0,1,0,0,0,1}, {0,1,0,1,0,0,0,1}, {1,1,0,1,0,0,0,1},
{0,0,1,1,0,0,0,1}, {1,0,1,1,0,0,0,1}, {0,1,1,1,0,0,0,1}, {1,1,1,1,0,0,0,1},
{0,0,0,0,1,0,0,1}, {1,0,0,0,1,0,0,1}, {0,1,0,0,1,0,0,1}, {1,1,0,0,1,0,0,1},
{0,0,1,0,1,0,0,1}, {1,0,1,0,1,0,0,1}, {0,1,1,0,1,0,0,1}, {1,1,1,0,1,0,0,1},
{0,0,0,1,1,0,0,1}, {1,0,0,1,1,0,0,1}, {0,1,0,1,1,0,0,1}, {1,1,0,1,1,0,0,1},
{0,0,1,1,1,0,0,1}, {1,0,1,1,1,0,0,1}, {0,1,1,1,1,0,0,1}, {1,1,1,1,1,0,0,1},
{0,0,0,0,0,1,0,1}, {1,0,0,0,0,1,0,1}, {0,1,0,0,0,1,0,1}, {1,1,0,0,0,1,0,1},
{0,0,1,0,0,1,0,1}, {1,0,1,0,0,1,0,1}, {0,1,1,0,0,1,0,1}, {1,1,1,0,0,1,0,1},
{0,0,0,1,0,1,0,1}, {1,0,0,1,0,1,0,1}, {0,1,0,1,0,1,0,1}, {1,1,0,1,0,1,0,1},
{0,0,1,1,0,1,0,1}, {1,0,1,1,0,1,0,1}, {0,1,1,1,0,1,0,1}, {1,1,1,1,0,1,0,1},
{0,0,0,0,1,1,0,1}, {1,0,0,0,1,1,0,1}, {0,1,0,0,1,1,0,1}, {1,1,0,0,1,1,0,1},
{0,0,1,0,1,1,0,1}, {1,0,1,0,1,1,0,1}, {0,1,1,0,1,1,0,1}, {1,1,1,0,1,1,0,1},
{0,0,0,1,1,1,0,1}, {1,0,0,1,1,1,0,1}, {0,1,0,1,1,1,0,1}, {1,1,0,1,1,1,0,1},
{0,0,1,1,1,1,0,1}, {1,0,1,1,1,1,0,1}, {0,1,1,1,1,1,0,1}, {1,1,1,1,1,1,0,1},
{0,0,0,0,0,0,1,1}, {1,0,0,0,0,0,1,1}, {0,1,0,0,0,0,1,1}, {1,1,0,0,0,0,1,1},
{0,0,1,0,0,0,1,1}, {1,0,1,0,0,0,1,1}, {0,1,1,0,0,0,1,1}, {1,1,1,0,0,0,1,1},
{0,0,0,1,0,0,1,1}, {1,0,0,1,0,0,1,1}, {0,1,0,1,0,0,1,1}, {1,1,0,1,0,0,1,1},
{0,0,1,1,0,0,1,1}, {1,0,1,1,0,0,1,1}, {0,1,1,1,0,0,1,1}, {1,1,1,1,0,0,1,1},
{0,0,0,0,1,0,1,1}, {1,0,0,0,1,0,1,1}, {0,1,0,0,1,0,1,1}, {1,1,0,0,1,0,1,1},
{0,0,1,0,1,0,1,1}, {1,0,1,0,1,0,1,1}, {0,1,1,0,1,0,1,1}, {1,1,1,0,1,0,1,1},
{0,0,0,1,1,0,1,1}, {1,0,0,1,1,0,1,1}, {0,1,0,1,1,0,1,1}, {1,1,0,1,1,0,1,1},
{0,0,1,1,1,0,1,1}, {1,0,1,1,1,0,1,1}, {0,1,1,1,1,0,1,1}, {1,1,1,1,1,0,1,1},
{0,0,0,0,0,1,1,1}, {1,0,0,0,0,1,1,1}, {0,1,0,0,0,1,1,1}, {1,1,0,0,0,1,1,1},
{0,0,1,0,0,1,1,1}, {1,0,1,0,0,1,1,1}, {0,1,1,0,0,1,1,1}, {1,1,1,0,0,1,1,1},
{0,0,0,1,0,1,1,1}, {1,0,0,1,0,1,1,1}, {0,1,0,1,0,1,1,1}, {1,1,0,1,0,1,1,1},
{0,0,1,1,0,1,1,1}, {1,0,1,1,0,1,1,1}, {0,1,1,1,0,1,1,1}, {1,1,1,1,0,1,1,1},
{0,0,0,0,1,1,1,1}, {1,0,0,0,1,1,1,1}, {0,1,0,0,1,1,1,1}, {1,1,0,0,1,1,1,1},
{0,0,1,0,1,1,1,1}, {1,0,1,0,1,1,1,1}, {0,1,1,0,1,1,1,1}, {1,1,1,0,1,1,1,1},
{0,0,0,1,1,1,1,1}, {1,0,0,1,1,1,1,1}, {0,1,0,1,1,1,1,1}, {1,1,0,1,1,1,1,1},
{0,0,1,1,1,1,1,1}, {1,0,1,1,1,1,1,1}, {0,1,1,1,1,1,1,1}, {1,1,1,1,1,1,1,1}}
-- Return a number that is the result of interpreting the table tbl (msb first)
local function tbl_to_number(tbl)
local n = #tbl
local rslt = 0
local power = 1
for i = 1, n do
rslt = rslt + tbl[i]*power
power = power*2
end
return rslt
end
-- Calculate bitwise xor of bytes m and n. 0 <= m,n <= 256.
local function bit_xor(m, n)
local tbl_m = cclxvi[m]
local tbl_n = cclxvi[n]
local tbl = {}
for i = 1, 8 do
if(tbl_m[i] ~= tbl_n[i]) then
tbl[i] = 1
else
tbl[i] = 0
end
end
return tbl_to_number(tbl)
end
-- Return the binary representation of the number x with the width of `digits`.
local function binary(x,digits)
local s=string.format("%o",x)
local a={["0"]="000",["1"]="001", ["2"]="010",["3"]="011",
["4"]="100",["5"]="101", ["6"]="110",["7"]="111"}
s=string.gsub(s,"(.)",function (d) return a[d] end)
-- remove leading 0s
s = string.gsub(s,"^0*(.*)$","%1")
local fmtstring = string.format("%%%ds",digits)
local ret = string.format(fmtstring,s)
return string.gsub(ret," ","0")
end
-- A small helper function for add_typeinfo_to_matrix() and add_version_information()
-- Add a 2 (black by default) / -2 (blank by default) to the matrix at position x,y
-- depending on the bitstring (size 1!) where "0"=blank and "1"=black.
local function fill_matrix_position(matrix,bitstring,x,y)
if bitstring == "1" then
matrix[x][y] = 2
else
matrix[x][y] = -2
end
end
--- Step 1: Determine version, ec level and mode for codeword
--- ========================================================
---
--- First we need to find out the version (= size) of the QR code. This depends on
--- the input data (the mode to be used), the requested error correction level
--- (normally we use the maximum level that fits into the minimal size).
-- Return the mode for the given string `str`.
-- See table 2 of the spec. We only support mode 1, 2 and 4.
-- That is: numeric, alaphnumeric and binary.
local function get_mode( str )
if string.match(str,"^[0-9]+$") then
return 1
elseif string.match(str,"^[0-9A-Z $%%*./:+-]+$") then
return 2
else
return 4
end
assert(false,"never reached") -- luacheck: ignore
return nil
end
--- Capacity of QR codes
--- --------------------
--- The capacity is calculated as follow: \\(\text{Number of data bits} = \text{number of codewords} * 8\\).
--- The number of data bits is now reduced by 4 (the mode indicator) and the length string,
--- that varies between 8 and 16, depending on the version and the mode (see method `get_length()`). The
--- remaining capacity is multiplied by the amount of data per bit string (numeric: 3, alphanumeric: 2, other: 1)
--- and divided by the length of the bit string (numeric: 10, alphanumeric: 11, binary: 8, kanji: 13).
--- Then the floor function is applied to the result:
--- $$\Big\lfloor \frac{( \text{#data bits} - 4 - \text{length string}) * \text{data per bit string}}{\text{length of the bit string}} \Big\rfloor$$
---
--- There is one problem remaining. The length string depends on the version,
--- and the version depends on the length string. But we take this into account when calculating the
--- the capacity, so this is not really a problem here.
-- The capacity (number of codewords) of each version (1-40) for error correction levels 1-4 (LMQH).
-- The higher the ec level, the lower the capacity of the version. Taken from spec, tables 7-11.
local capacity = {
{ 19, 16, 13, 9},{ 34, 28, 22, 16},{ 55, 44, 34, 26},{ 80, 64, 48, 36},
{ 108, 86, 62, 46},{ 136, 108, 76, 60},{ 156, 124, 88, 66},{ 194, 154, 110, 86},
{ 232, 182, 132, 100},{ 274, 216, 154, 122},{ 324, 254, 180, 140},{ 370, 290, 206, 158},
{ 428, 334, 244, 180},{ 461, 365, 261, 197},{ 523, 415, 295, 223},{ 589, 453, 325, 253},
{ 647, 507, 367, 283},{ 721, 563, 397, 313},{ 795, 627, 445, 341},{ 861, 669, 485, 385},
{ 932, 714, 512, 406},{1006, 782, 568, 442},{1094, 860, 614, 464},{1174, 914, 664, 514},
{1276, 1000, 718, 538},{1370, 1062, 754, 596},{1468, 1128, 808, 628},{1531, 1193, 871, 661},
{1631, 1267, 911, 701},{1735, 1373, 985, 745},{1843, 1455, 1033, 793},{1955, 1541, 1115, 845},
{2071, 1631, 1171, 901},{2191, 1725, 1231, 961},{2306, 1812, 1286, 986},{2434, 1914, 1354, 1054},
{2566, 1992, 1426, 1096},{2702, 2102, 1502, 1142},{2812, 2216, 1582, 1222},{2956, 2334, 1666, 1276}}
--- Return the smallest version for this codeword. If `requested_ec_level` is supplied,
--- then the ec level (LMQH - 1,2,3,4) must be at least the requested level.
-- mode = 1,2,4,8
local function get_version_eclevel(len,mode,requested_ec_level)
local local_mode = mode
if mode == 4 then
local_mode = 3
elseif mode == 8 then
local_mode = 4
end
assert( local_mode <= 4 )
local bits, digits, modebits, c
local tab = { {10,9,8,8},{12,11,16,10},{14,13,16,12} }
local minversion = 40
local maxec_level = requested_ec_level or 1
local min,max = 1, 4
if requested_ec_level and requested_ec_level >= 1 and requested_ec_level <= 4 then
min = requested_ec_level
max = requested_ec_level
end
for ec_level=min,max do
for version=1,#capacity do
bits = capacity[version][ec_level] * 8
bits = bits - 4 -- the mode indicator
if version < 10 then
digits = tab[1][local_mode]
elseif version < 27 then
digits = tab[2][local_mode]
elseif version <= 40 then
digits = tab[3][local_mode]
end
modebits = bits - digits
if local_mode == 1 then -- numeric
c = math.floor(modebits * 3 / 10)
elseif local_mode == 2 then -- alphanumeric
c = math.floor(modebits * 2 / 11)
elseif local_mode == 3 then -- binary
c = math.floor(modebits * 1 / 8)
else
c = math.floor(modebits * 1 / 13)
end
if c >= len then
if version <= minversion then
minversion = version
maxec_level = ec_level
end
break
end
end
end
return minversion, maxec_level
end
-- Return a bit string of 0s and 1s that includes the length of the code string.
-- The modes are numeric = 1, alphanumeric = 2, binary = 4, and japanese = 8
local function get_length(str,version,mode)
local i = mode
if mode == 4 then
i = 3
elseif mode == 8 then
i = 4
end
assert( i <= 4 )
local tab = { {10,9,8,8},{12,11,16,10},{14,13,16,12} }
local digits
if version < 10 then
digits = tab[1][i]
elseif version < 27 then
digits = tab[2][i]
elseif version <= 40 then
digits = tab[3][i]
else
assert(false, "get_length, version > 40 not supported")
end
local len = binary(#str,digits)
return len
end
--- If the `requested_ec_level` or the `mode` are provided, this will be used if possible.
--- The mode depends on the characters used in the string `str`. It seems to be
--- possible to split the QR code to handle multiple modes, but we don't do that.
local function get_version_eclevel_mode_bistringlength(str,requested_ec_level,mode)
local local_mode
if mode then
assert(false,"not implemented")
-- check if the mode is OK for the string
local_mode = mode
else
local_mode = get_mode(str)
end
local version, ec_level
version, ec_level = get_version_eclevel(#str,local_mode,requested_ec_level)
local length_string = get_length(str,version,local_mode)
return version,ec_level,binary(local_mode,4),local_mode,length_string
end
--- Step 2: Encode data
--- ===================
--- There are several ways to encode the data. We currently support only numeric, alphanumeric and binary.
--- We already chose the encoding (a.k.a. mode) in the first step, so we need to apply the mode to the
--- codeword.
---
--- **Numeric**: take three digits and encode them in 10 bits
--- **Alphanumeric**: take two characters and encode them in 11 bits
--- **Binary**: take one octet and encode it in 8 bits
local asciitbl = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -- 0x01-0x0f
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -- 0x10-0x1f
36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, -- 0x20-0x2f
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, -- 0x30-0x3f
-1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, -- 0x40-0x4f
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -- 0x50-0x5f
}
-- Return a binary representation of the numeric string `str`. This must contain only digits 0-9.
local function encode_string_numeric(str)
local bitstring = ""
local int
string.gsub(str,"..?.?",function(a)
int = tonumber(a)
if #a == 3 then
bitstring = bitstring .. binary(int,10)
elseif #a == 2 then
bitstring = bitstring .. binary(int,7)
else
bitstring = bitstring .. binary(int,4)
end
end)
return bitstring
end
-- Return a binary representation of the alphanumeric string `str`. This must contain only
-- digits 0-9, uppercase letters A-Z, space and the following chars: $%*./:+-.
local function encode_string_ascii(str)
local bitstring = ""
local int
local b1, b2
string.gsub(str,"..?",function(a)
if #a == 2 then
b1 = asciitbl[string.byte(string.sub(a,1,1))]
b2 = asciitbl[string.byte(string.sub(a,2,2))]
int = b1 * 45 + b2
bitstring = bitstring .. binary(int,11)
else
int = asciitbl[string.byte(a)]
bitstring = bitstring .. binary(int,6)
end
end)
return bitstring
end
-- Return a bitstring representing string str in binary mode.
-- We don't handle UTF-8 in any special way because we assume the
-- scanner recognizes UTF-8 and displays it correctly.
local function encode_string_binary(str)
local ret = {}
string.gsub(str,".",function(x)
ret[#ret + 1] = binary(string.byte(x),8)
end)
return table.concat(ret)
end
-- Return a bitstring representing string str in the given mode.
local function encode_data(str,mode)
if mode == 1 then
return encode_string_numeric(str)
elseif mode == 2 then
return encode_string_ascii(str)
elseif mode == 4 then
return encode_string_binary(str)
else
assert(false,"not implemented yet")
end
end
-- Encoding the codeword is not enough. We need to make sure that
-- the length of the binary string is equal to the number of codewords of the version.
local function add_pad_data(version,ec_level,data)
local count_to_pad, missing_digits
local cpty = capacity[version][ec_level] * 8
count_to_pad = math.min(4,cpty - #data)
if count_to_pad > 0 then
data = data .. string.rep("0",count_to_pad)
end
if math.fmod(#data,8) ~= 0 then
missing_digits = 8 - math.fmod(#data,8)
data = data .. string.rep("0",missing_digits)
end
assert(math.fmod(#data,8) == 0)
-- add "11101100" and "00010001" until enough data
while #data < cpty do
data = data .. "11101100"
if #data < cpty then
data = data .. "00010001"
end
end
return data
end
--- Step 3: Organize data and calculate error correction code
--- =======================================================
--- The data in the qrcode is not encoded linearly. For example code 5-H has four blocks, the first two blocks
--- contain 11 codewords and 22 error correction codes each, the second block contain 12 codewords and 22 ec codes each.
--- We just take the table from the spec and don't calculate the blocks ourself. The table `ecblocks` contains this info.
---
--- During the phase of splitting the data into codewords, we do the calculation for error correction codes. This step involves
--- polynomial division. Find a math book from school and follow the code here :)
--- ### Reed Solomon error correction
--- Now this is the slightly ugly part of the error correction. We start with log/antilog tables
-- https://codyplanteen.com/assets/rs/gf256_log_antilog.pdf
local alpha_int = {
[0] = 1,
2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76,
152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 157,
39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35, 70,
140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, 95,
190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, 253,
231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217,
175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206, 129,
31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204, 133,
23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, 168,
77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 230,
209, 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227,
219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65, 130,
25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166, 81,
162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 18,
36, 72, 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 44,
88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 0, 0
}
local int_alpha = {
[0] = 256, -- special value
0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4,
100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, 5,
138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69, 29,
181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166, 6,
191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, 54,
208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30,
66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61, 202,
94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87, 7,
112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24, 227,
165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, 55,
63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242,
86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, 31,
45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246, 108,
161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90, 203,
89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, 79,
174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175
}
-- We only need the polynomial generators for block sizes 7, 10, 13, 15, 16, 17, 18, 20, 22, 24, 26, 28, and 30. Version
-- 2 of the qr codes don't need larger ones (as opposed to version 1). The table has the format x^1*ɑ^21 + x^2*a^102 ...
local generator_polynomial = {
[7] = { 21, 102, 238, 149, 146, 229, 87, 0},
[10] = { 45, 32, 94, 64, 70, 118, 61, 46, 67, 251, 0 },
[13] = { 78, 140, 206, 218, 130, 104, 106, 100, 86, 100, 176, 152, 74, 0 },
[15] = {105, 99, 5, 124, 140, 237, 58, 58, 51, 37, 202, 91, 61, 183, 8, 0},
[16] = {120, 225, 194, 182, 169, 147, 191, 91, 3, 76, 161, 102, 109, 107, 104, 120, 0},
[17] = {136, 163, 243, 39, 150, 99, 24, 147, 214, 206, 123, 239, 43, 78, 206, 139, 43, 0},
[18] = {153, 96, 98, 5, 179, 252, 148, 152, 187, 79, 170, 118, 97, 184, 94, 158, 234, 215, 0},
[20] = {190, 188, 212, 212, 164, 156, 239, 83, 225, 221, 180, 202, 187, 26, 163, 61, 50, 79, 60, 17, 0},
[22] = {231, 165, 105, 160, 134, 219, 80, 98, 172, 8, 74, 200, 53, 221, 109, 14, 230, 93, 242, 247, 171, 210, 0},
[24] = { 21, 227, 96, 87, 232, 117, 0, 111, 218, 228, 226, 192, 152, 169, 180, 159, 126, 251, 117, 211, 48, 135, 121, 229, 0},
[26] = { 70, 218, 145, 153, 227, 48, 102, 13, 142, 245, 21, 161, 53, 165, 28, 111, 201, 145, 17, 118, 182, 103, 2, 158, 125, 173, 0},
[28] = {123, 9, 37, 242, 119, 212, 195, 42, 87, 245, 43, 21, 201, 232, 27, 205, 147, 195, 190, 110, 180, 108, 234, 224, 104, 200, 223, 168, 0},
[30] = {180, 192, 40, 238, 216, 251, 37, 156, 130, 224, 193, 226, 173, 42, 125, 222, 96, 239, 86, 110, 48, 50, 182, 179, 31, 216, 152, 145, 173, 41, 0}}
-- Turn a binary string of length 8*x into a table size x of numbers.
local function convert_bitstring_to_bytes(data)
local msg = {}
string.gsub(data,"(........)",function(x)
msg[#msg+1] = tonumber2(x,2)
end)
return msg
end
-- Return a table that has 0's in the first entries and then the alpha
-- representation of the generator polynominal
local function get_generator_polynominal_adjusted(num_ec_codewords,highest_exponent)
local gp_alpha = {[0]=0}
for i=0,highest_exponent - num_ec_codewords - 1 do
gp_alpha[i] = 0
end
local gp = generator_polynomial[num_ec_codewords]
for i=1,num_ec_codewords + 1 do
gp_alpha[highest_exponent - num_ec_codewords + i - 1] = gp[i]
end
return gp_alpha
end
--- These converter functions use the log/antilog table above.
--- We could have created the table programatically, but I like fixed tables.
-- Convert polynominal in int notation to alpha notation.
local function convert_to_alpha( tab )
local new_tab = {}
for i=0,#tab do
new_tab[i] = int_alpha[tab[i]]
end
return new_tab
end
-- Convert polynominal in alpha notation to int notation.
local function convert_to_int(tab)
local new_tab = {}
for i=0,#tab do
new_tab[i] = alpha_int[tab[i]]
end
return new_tab
end
-- That's the heart of the error correction calculation.
local function calculate_error_correction(data,num_ec_codewords)
local mp
if type(data)=="string" then
mp = convert_bitstring_to_bytes(data)
elseif type(data)=="table" then
mp = data
else
assert(false,string.format("Unknown type for data: %s",type(data)))
end
local len_message = #mp
local highest_exponent = len_message + num_ec_codewords - 1
local gp_alpha,tmp
local he
local gp_int, mp_alpha
local mp_int = {}
-- create message shifted to left (highest exponent)
for i=1,len_message do
mp_int[highest_exponent - i + 1] = mp[i]
end
for i=1,highest_exponent - len_message do
mp_int[i] = 0
end
mp_int[0] = 0
mp_alpha = convert_to_alpha(mp_int)
local zaehler = 0
while highest_exponent >= num_ec_codewords do
feed_the_dog()
zaehler = zaehler + 1
if zaehler % 3 == 0 and CALCULATE_QRCODE_FOR_REPORT == 1 then
if file_open ~= nil then
start_timer(26,WAIT_26,0,1)
error2("qrcode_0001")
coroutine.yield()
else
notify("QR - Error correction, WAIT26")
end
end
gp_alpha = get_generator_polynominal_adjusted(num_ec_codewords,highest_exponent)
-- Multiply generator polynomial by first coefficient of the above polynomial
-- take the highest exponent from the message polynom (alpha) and add
-- it to the generator polynom
local exp = mp_alpha[highest_exponent]
for i=highest_exponent,highest_exponent - num_ec_codewords,-1 do
if exp ~= 256 then
if gp_alpha[i] + exp >= 255 then
gp_alpha[i] = math.fmod(gp_alpha[i] + exp,255)
else
gp_alpha[i] = gp_alpha[i] + exp
end
else
gp_alpha[i] = 256
end
end
for i=highest_exponent - num_ec_codewords - 1,0,-1 do
gp_alpha[i] = 256
end
gp_int = convert_to_int(gp_alpha)
mp_int = convert_to_int(mp_alpha)
tmp = {}
for i=highest_exponent,0,-1 do
tmp[i] = bit_xor(gp_int[i],mp_int[i])
end
-- remove leading 0's
he = highest_exponent
for i=he,0,-1 do
-- We need to stop if the length of the codeword is matched
if i < num_ec_codewords then break end
if tmp[i] == 0 then
tmp[i] = nil
highest_exponent = highest_exponent - 1
else
break
end
end
mp_int = tmp
mp_alpha = convert_to_alpha(mp_int)
end
local ret = {}
-- reverse data
for i=#mp_int,0,-1 do
ret[#ret + 1] = mp_int[i]
end
return ret
end
--- #### Arranging the data
--- Now we arrange the data into smaller chunks. This table is taken from the spec.
-- ecblocks has 40 entries, one for each version. Each version entry has 4 entries, for each LMQH
-- ec level. Each entry has two or four fields, the odd files are the number of repetitions for the
-- folowing block info. The first entry of the block is the total number of codewords in the block,
-- the second entry is the number of data codewords. The third is not important.
local ecblocks = {
{{ 1,{ 26, 19, 2} }, { 1,{26,16, 4}}, { 1,{26,13, 6}}, { 1, {26, 9, 8} }},
{{ 1,{ 44, 34, 4} }, { 1,{44,28, 8}}, { 1,{44,22,11}}, { 1, {44,16,14} }},
{{ 1,{ 70, 55, 7} }, { 1,{70,44,13}}, { 2,{35,17, 9}}, { 2, {35,13,11} }},
{{ 1,{100, 80,10} }, { 2,{50,32, 9}}, { 2,{50,24,13}}, { 4, {25, 9, 8} }},
{{ 1,{134,108,13} }, { 2,{67,43,12}}, { 2,{33,15, 9}, 2,{34,16, 9}}, { 2, {33,11,11}, 2,{34,12,11}}},
{{ 2,{ 86, 68, 9} }, { 4,{43,27, 8}}, { 4,{43,19,12}}, { 4, {43,15,14} }},
{{ 2,{ 98, 78,10} }, { 4,{49,31, 9}}, { 2,{32,14, 9}, 4,{33,15, 9}}, { 4, {39,13,13}, 1,{40,14,13}}},
{{ 2,{121, 97,12} }, { 2,{60,38,11}, 2,{61,39,11}}, { 4,{40,18,11}, 2,{41,19,11}}, { 4, {40,14,13}, 2,{41,15,13}}},
{{ 2,{146,116,15} }, { 3,{58,36,11}, 2,{59,37,11}}, { 4,{36,16,10}, 4,{37,17,10}}, { 4, {36,12,12}, 4,{37,13,12}}},
{{ 2,{ 86, 68, 9}, 2,{ 87, 69, 9}}, { 4,{69,43,13}, 1,{70,44,13}}, { 6,{43,19,12}, 2,{44,20,12}}, { 6, {43,15,14}, 2,{44,16,14}}},
{{ 4,{101, 81,10} }, { 1,{80,50,15}, 4,{81,51,15}}, { 4,{50,22,14}, 4,{51,23,14}}, { 3, {36,12,12}, 8,{37,13,12}}},
{{ 2,{116, 92,12}, 2,{117, 93,12}}, { 6,{58,36,11}, 2,{59,37,11}}, { 4,{46,20,13}, 6,{47,21,13}}, { 7, {42,14,14}, 4,{43,15,14}}},
{{ 4,{133,107,13} }, { 8,{59,37,11}, 1,{60,38,11}}, { 8,{44,20,12}, 4,{45,21,12}}, { 12, {33,11,11}, 4,{34,12,11}}},
{{ 3,{145,115,15}, 1,{146,116,15}}, { 4,{64,40,12}, 5,{65,41,12}}, { 11,{36,16,10}, 5,{37,17,10}}, { 11, {36,12,12}, 5,{37,13,12}}},
{{ 5,{109, 87,11}, 1,{110, 88,11}}, { 5,{65,41,12}, 5,{66,42,12}}, { 5,{54,24,15}, 7,{55,25,15}}, { 11, {36,12,12}, 7,{37,13,12}}},
{{ 5,{122, 98,12}, 1,{123, 99,12}}, { 7,{73,45,14}, 3,{74,46,14}}, { 15,{43,19,12}, 2,{44,20,12}}, { 3, {45,15,15}, 13,{46,16,15}}},
{{ 1,{135,107,14}, 5,{136,108,14}}, { 10,{74,46,14}, 1,{75,47,14}}, { 1,{50,22,14}, 15,{51,23,14}}, { 2, {42,14,14}, 17,{43,15,14}}},
{{ 5,{150,120,15}, 1,{151,121,15}}, { 9,{69,43,13}, 4,{70,44,13}}, { 17,{50,22,14}, 1,{51,23,14}}, { 2, {42,14,14}, 19,{43,15,14}}},
{{ 3,{141,113,14}, 4,{142,114,14}}, { 3,{70,44,13}, 11,{71,45,13}}, { 17,{47,21,13}, 4,{48,22,13}}, { 9, {39,13,13}, 16,{40,14,13}}},
{{ 3,{135,107,14}, 5,{136,108,14}}, { 3,{67,41,13}, 13,{68,42,13}}, { 15,{54,24,15}, 5,{55,25,15}}, { 15, {43,15,14}, 10,{44,16,14}}},
{{ 4,{144,116,14}, 4,{145,117,14}}, { 17,{68,42,13}}, { 17,{50,22,14}, 6,{51,23,14}}, { 19, {46,16,15}, 6,{47,17,15}}},
{{ 2,{139,111,14}, 7,{140,112,14}}, { 17,{74,46,14}}, { 7,{54,24,15}, 16,{55,25,15}}, { 34, {37,13,12} }},
{{ 4,{151,121,15}, 5,{152,122,15}}, { 4,{75,47,14}, 14,{76,48,14}}, { 11,{54,24,15}, 14,{55,25,15}}, { 16, {45,15,15}, 14,{46,16,15}}},
{{ 6,{147,117,15}, 4,{148,118,15}}, { 6,{73,45,14}, 14,{74,46,14}}, { 11,{54,24,15}, 16,{55,25,15}}, { 30, {46,16,15}, 2,{47,17,15}}},
{{ 8,{132,106,13}, 4,{133,107,13}}, { 8,{75,47,14}, 13,{76,48,14}}, { 7,{54,24,15}, 22,{55,25,15}}, { 22, {45,15,15}, 13,{46,16,15}}},
{{ 10,{142,114,14}, 2,{143,115,14}}, { 19,{74,46,14}, 4,{75,47,14}}, { 28,{50,22,14}, 6,{51,23,14}}, { 33, {46,16,15}, 4,{47,17,15}}},
{{ 8,{152,122,15}, 4,{153,123,15}}, { 22,{73,45,14}, 3,{74,46,14}}, { 8,{53,23,15}, 26,{54,24,15}}, { 12, {45,15,15}, 28,{46,16,15}}},
{{ 3,{147,117,15}, 10,{148,118,15}}, { 3,{73,45,14}, 23,{74,46,14}}, { 4,{54,24,15}, 31,{55,25,15}}, { 11, {45,15,15}, 31,{46,16,15}}},
{{ 7,{146,116,15}, 7,{147,117,15}}, { 21,{73,45,14}, 7,{74,46,14}}, { 1,{53,23,15}, 37,{54,24,15}}, { 19, {45,15,15}, 26,{46,16,15}}},
{{ 5,{145,115,15}, 10,{146,116,15}}, { 19,{75,47,14}, 10,{76,48,14}}, { 15,{54,24,15}, 25,{55,25,15}}, { 23, {45,15,15}, 25,{46,16,15}}},
{{ 13,{145,115,15}, 3,{146,116,15}}, { 2,{74,46,14}, 29,{75,47,14}}, { 42,{54,24,15}, 1,{55,25,15}}, { 23, {45,15,15}, 28,{46,16,15}}},
{{ 17,{145,115,15} }, { 10,{74,46,14}, 23,{75,47,14}}, { 10,{54,24,15}, 35,{55,25,15}}, { 19, {45,15,15}, 35,{46,16,15}}},
{{ 17,{145,115,15}, 1,{146,116,15}}, { 14,{74,46,14}, 21,{75,47,14}}, { 29,{54,24,15}, 19,{55,25,15}}, { 11, {45,15,15}, 46,{46,16,15}}},
{{ 13,{145,115,15}, 6,{146,116,15}}, { 14,{74,46,14}, 23,{75,47,14}}, { 44,{54,24,15}, 7,{55,25,15}}, { 59, {46,16,15}, 1,{47,17,15}}},
{{ 12,{151,121,15}, 7,{152,122,15}}, { 12,{75,47,14}, 26,{76,48,14}}, { 39,{54,24,15}, 14,{55,25,15}}, { 22, {45,15,15}, 41,{46,16,15}}},
{{ 6,{151,121,15}, 14,{152,122,15}}, { 6,{75,47,14}, 34,{76,48,14}}, { 46,{54,24,15}, 10,{55,25,15}}, { 2, {45,15,15}, 64,{46,16,15}}},
{{ 17,{152,122,15}, 4,{153,123,15}}, { 29,{74,46,14}, 14,{75,47,14}}, { 49,{54,24,15}, 10,{55,25,15}}, { 24, {45,15,15}, 46,{46,16,15}}},
{{ 4,{152,122,15}, 18,{153,123,15}}, { 13,{74,46,14}, 32,{75,47,14}}, { 48,{54,24,15}, 14,{55,25,15}}, { 42, {45,15,15}, 32,{46,16,15}}},
{{ 20,{147,117,15}, 4,{148,118,15}}, { 40,{75,47,14}, 7,{76,48,14}}, { 43,{54,24,15}, 22,{55,25,15}}, { 10, {45,15,15}, 67,{46,16,15}}},
{{ 19,{148,118,15}, 6,{149,119,15}}, { 18,{75,47,14}, 31,{76,48,14}}, { 34,{54,24,15}, 34,{55,25,15}}, { 20, {45,15,15}, 61,{46,16,15}}}
}
-- The bits that must be 0 if the version does fill the complete matrix.
-- Example: for version 1, no bits need to be added after arranging the data, for version 2 we need to add 7 bits at the end.
local remainder = {0, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0}
-- This is the formula for table 1 in the spec:
-- function get_capacity_remainder( version )
-- local len = version * 4 + 17
-- local size = len^2
-- local function_pattern_modules = 192 + 2 * len - 32 -- Position Adjustment pattern + timing pattern
-- local count_alignemnt_pattern = #alignment_pattern[version]
-- if count_alignemnt_pattern > 0 then
-- -- add 25 for each aligment pattern
-- function_pattern_modules = function_pattern_modules + 25 * ( count_alignemnt_pattern^2 - 3 )
-- -- but substract the timing pattern occupied by the aligment pattern on the top and left
-- function_pattern_modules = function_pattern_modules - ( count_alignemnt_pattern - 2) * 10
-- end
-- size = size - function_pattern_modules
-- if version > 6 then
-- size = size - 67
-- else
-- size = size - 31
-- end
-- return math.floor(size/8),math.fmod(size,8)
-- end
--- Example: Version 5-H has four data and four error correction blocks. The table above lists
--- `2, {33,11,11}, 2,{34,12,11}` for entry [5][4]. This means we take two blocks with 11 codewords
--- and two blocks with 12 codewords, and two blocks with 33 - 11 = 22 ec codes and another
--- two blocks with 34 - 12 = 22 ec codes.
--- Block 1: D1 D2 D3 ... D11
--- Block 2: D12 D13 D14 ... D22
--- Block 3: D23 D24 D25 ... D33 D34
--- Block 4: D35 D36 D37 ... D45 D46
--- Then we place the data like this in the matrix: D1, D12, D23, D35, D2, D13, D24, D36 ... D45, D34, D46. The same goes
--- with error correction codes.
-- The given data can be a string of 0's and 1' (with #string mod 8 == 0).
-- Alternatively the data can be a table of codewords. The number of codewords
-- must match the capacity of the qr code.
local function arrange_codewords_and_calculate_ec( version,ec_level,data )
if type(data)=="table" then
local tmp = ""
for i=1,#data do
tmp = tmp .. binary(data[i],8)
end
data = tmp
end
-- If the size of the data is not enough for the codeword, we add 0's and two special bytes until finished.
local blocks = ecblocks[version][ec_level]
local size_datablock_bytes, size_ecblock_bytes
local datablocks = {}
local final_ecblocks = {}
local count = 1
local pos = 0
local cpty_ec_bits = 0
for i=1,#blocks/2 do
for _=1,blocks[2*i - 1] do
feed_the_dog()
if CALCULATE_QRCODE_FOR_REPORT == 1 then
if file_open ~= nil then
start_timer(26,WAIT_26,0,1)
error2("qrcode_0002")
coroutine.yield()
else
notify("QR Code - Codewords, WAIT26")
end
end
size_datablock_bytes = blocks[2*i][2]
size_ecblock_bytes = blocks[2*i][1] - blocks[2*i][2]
cpty_ec_bits = cpty_ec_bits + size_ecblock_bytes * 8
datablocks[#datablocks + 1] = string.sub(data, pos * 8 + 1,( pos + size_datablock_bytes)*8)
local tmp_tab = calculate_error_correction(datablocks[#datablocks],size_ecblock_bytes)
local tmp_str = ""
for x=1,#tmp_tab do
tmp_str = tmp_str .. binary(tmp_tab[x],8)
end
final_ecblocks[#final_ecblocks + 1] = tmp_str
pos = pos + size_datablock_bytes
count = count + 1
end
end
local arranged_data = ""
pos = 1
repeat
for i=1,#datablocks do
if pos < #datablocks[i] then
arranged_data = arranged_data .. string.sub(datablocks[i],pos, pos + 7)
end
end
pos = pos + 8
until #arranged_data == #data
-- ec
local arranged_ec = ""
pos = 1
repeat
for i=1,#final_ecblocks do
if pos < #final_ecblocks[i] then
arranged_ec = arranged_ec .. string.sub(final_ecblocks[i],pos, pos + 7)
end
end
pos = pos + 8
until #arranged_ec == cpty_ec_bits
return arranged_data .. arranged_ec
end
--- Step 4: Generate 8 matrices with different masks and calculate the penalty
--- ==========================================================================
---
--- Prepare matrix
--- --------------
--- The first step is to prepare an _empty_ matrix for a given size/mask. The matrix has a
--- few predefined areas that must be black or blank. We encode the matrix with a two
--- dimensional field where the numbers determine which pixel is blank or not.
---
--- The following code is used for our matrix:
--- 0 = not in use yet,
--- -2 = blank by mandatory pattern,
--- 2 = black by mandatory pattern,
--- -1 = blank by data,
--- 1 = black by data
---
---
--- To prepare the _empty_, we add positioning, alingment and timing patters.
--- ### Positioning patterns ###
local function add_position_detection_patterns(tab_x)
local size = #tab_x
-- allocate quite zone in the matrix area
for i=1,8 do
for j=1,8 do
tab_x[i][j] = -2
tab_x[size - 8 + i][j] = -2
tab_x[i][size - 8 + j] = -2
end
end
-- draw the detection pattern (outer)
for i=1,7 do
-- top left
tab_x[1][i]=2
tab_x[7][i]=2
tab_x[i][1]=2
tab_x[i][7]=2
-- top right
tab_x[size][i]=2
tab_x[size - 6][i]=2
tab_x[size - i + 1][1]=2
tab_x[size - i + 1][7]=2
-- bottom left
tab_x[1][size - i + 1]=2
tab_x[7][size - i + 1]=2
tab_x[i][size - 6]=2
tab_x[i][size]=2
end
-- draw the detection pattern (inner)
for i=1,3 do
for j=1,3 do
-- top left
tab_x[2+j][i+2]=2
-- top right
tab_x[size - j - 1][i+2]=2
-- bottom left
tab_x[2 + j][size - i - 1]=2
end
end
end
--- ### Timing patterns ###
-- The timing patterns (two) are the dashed lines between two adjacent positioning patterns on row/column 7.
local function add_timing_pattern(tab_x)
local line,col
line = 7
col = 9
for i=col,#tab_x - 8 do
if math.fmod(i,2) == 1 then
tab_x[i][line] = 2
else
tab_x[i][line] = -2
end
end
for i=col,#tab_x - 8 do
if math.fmod(i,2) == 1 then
tab_x[line][i] = 2
else
tab_x[line][i] = -2
end
end
end
--- ### Alignment patterns ###
--- The alignment patterns must be added to the matrix for versions > 1. The amount and positions depend on the versions and are
--- given by the spec. Beware: the patterns must not be placed where we have the positioning patterns
--- (that is: top left, top right and bottom left.)
-- For each version, where should we place the alignment patterns? See table E.1 of the spec
local alignment_pattern = {
{},{6,18},{6,22},{6,26},{6,30},{6,34}, -- 1-6
{6,22,38},{6,24,42},{6,26,46},{6,28,50},{6,30,54},{6,32,58},{6,34,62}, -- 7-13
{6,26,46,66},{6,26,48,70},{6,26,50,74},{6,30,54,78},{6,30,56,82},{6,30,58,86},{6,34,62,90}, -- 14-20
{6,28,50,72,94},{6,26,50,74,98},{6,30,54,78,102},{6,28,54,80,106},{6,32,58,84,110},{6,30,58,86,114},{6,34,62,90,118}, -- 21-27
{6,26,50,74,98 ,122},{6,30,54,78,102,126},{6,26,52,78,104,130},{6,30,56,82,108,134},{6,34,60,86,112,138},{6,30,58,86,114,142},{6,34,62,90,118,146}, -- 28-34
{6,30,54,78,102,126,150}, {6,24,50,76,102,128,154},{6,28,54,80,106,132,158},{6,32,58,84,110,136,162},{6,26,54,82,110,138,166},{6,30,58,86,114,142,170} -- 35 - 40
}
--- The alignment pattern has size 5x5 and looks like this:
--- XXXXX
--- X X
--- X X X
--- X X
--- XXXXX
local function add_alignment_pattern( tab_x )
local version = (#tab_x - 17) / 4
local ap = alignment_pattern[version]
local pos_x, pos_y
for x=1,#ap do
for y=1,#ap do
-- we must not put an alignment pattern on top of the positioning pattern
if not (x == 1 and y == 1 or x == #ap and y == 1 or x == 1 and y == #ap ) then
pos_x = ap[x] + 1
pos_y = ap[y] + 1
tab_x[pos_x][pos_y] = 2
tab_x[pos_x+1][pos_y] = -2
tab_x[pos_x-1][pos_y] = -2
tab_x[pos_x+2][pos_y] = 2
tab_x[pos_x-2][pos_y] = 2
tab_x[pos_x ][pos_y - 2] = 2
tab_x[pos_x+1][pos_y - 2] = 2
tab_x[pos_x-1][pos_y - 2] = 2
tab_x[pos_x+2][pos_y - 2] = 2
tab_x[pos_x-2][pos_y - 2] = 2
tab_x[pos_x ][pos_y + 2] = 2
tab_x[pos_x+1][pos_y + 2] = 2
tab_x[pos_x-1][pos_y + 2] = 2
tab_x[pos_x+2][pos_y + 2] = 2
tab_x[pos_x-2][pos_y + 2] = 2
tab_x[pos_x ][pos_y - 1] = -2
tab_x[pos_x+1][pos_y - 1] = -2
tab_x[pos_x-1][pos_y - 1] = -2
tab_x[pos_x+2][pos_y - 1] = 2
tab_x[pos_x-2][pos_y - 1] = 2
tab_x[pos_x ][pos_y + 1] = -2
tab_x[pos_x+1][pos_y + 1] = -2
tab_x[pos_x-1][pos_y + 1] = -2
tab_x[pos_x+2][pos_y + 1] = 2
tab_x[pos_x-2][pos_y + 1] = 2
end
end
end
end
--- ### Type information ###
--- Let's not forget the type information that is in column 9 next to the left positioning patterns and on row 9 below
--- the top positioning patterns. This type information is not fixed, it depends on the mask and the error correction.
-- The first index is ec level (LMQH,1-4), the second is the mask (0-7). This bitstring of length 15 is to be used
-- as mandatory pattern in the qrcode. Mask -1 is for debugging purpose only and is the 'noop' mask.
local typeinfo = {
{ [-1]= "111111111111111", [0] = "111011111000100", "111001011110011", "111110110101010", "111100010011101", "110011000101111", "110001100011000", "110110001000001", "110100101110110" },
{ [-1]= "111111111111111", [0] = "101010000010010", "101000100100101", "101111001111100", "101101101001011", "100010111111001", "100000011001110", "100111110010111", "100101010100000" },
{ [-1]= "111111111111111", [0] = "011010101011111", "011000001101000", "011111100110001", "011101000000110", "010010010110100", "010000110000011", "010111011011010", "010101111101101" },
{ [-1]= "111111111111111", [0] = "001011010001001", "001001110111110", "001110011100111", "001100111010000", "000011101100010", "000001001010101", "000110100001100", "000100000111011" }
}
-- The typeinfo is a mixture of mask and ec level information and is
-- added twice to the qr code, one horizontal, one vertical.
local function add_typeinfo_to_matrix( matrix,ec_level,mask )
local ec_mask_type = typeinfo[ec_level][mask]
local bit
-- vertical from bottom to top
for i=1,7 do
bit = string.sub(ec_mask_type,i,i)
fill_matrix_position(matrix, bit, 9, #matrix - i + 1)
end
for i=8,9 do
bit = string.sub(ec_mask_type,i,i)
fill_matrix_position(matrix,bit,9,17-i)
end
for i=10,15 do
bit = string.sub(ec_mask_type,i,i)
fill_matrix_position(matrix,bit,9,16 - i)
end
-- horizontal, left to right
for i=1,6 do
bit = string.sub(ec_mask_type,i,i)
fill_matrix_position(matrix,bit,i,9)
end
bit = string.sub(ec_mask_type,7,7)
fill_matrix_position(matrix,bit,8,9)
for i=8,15 do
bit = string.sub(ec_mask_type,i,i)
fill_matrix_position(matrix,bit,#matrix - 15 + i,9)
end
end
-- Bits for version information 7-40
-- The reversed strings from https://www.thonky.com/qr-code-tutorial/format-version-tables
local version_information = {"001010010011111000", "001111011010000100", "100110010101100100", "110010110010010100",
"011011111101110100", "010001101110001100", "111000100001101100", "101100000110011100", "000101001001111100",
"000111101101000010", "101110100010100010", "111010000101010010", "010011001010110010", "011001011001001010",
"110000010110101010", "100100110001011010", "001101111110111010", "001000110111000110", "100001111000100110",
"110101011111010110", "011100010000110110", "010110000011001110", "111111001100101110", "101011101011011110",
"000010100100111110", "101010111001000001", "000011110110100001", "010111010001010001", "111110011110110001",
"110100001101001001", "011101000010101001", "001001100101011001", "100000101010111001", "100101100011000101" }
-- Versions 7 and above need two bitfields with version information added to the code
local function add_version_information(matrix,version)
if version < 7 then return end
local size = #matrix
local bitstring = version_information[version - 6]
local x,y, bit
local start_x, start_y
-- first top right
start_x = size - 10
start_y = 1
for i=1,#bitstring do
bit = string.sub(bitstring,i,i)
x = start_x + math.fmod(i - 1,3)
y = start_y + math.floor( (i - 1) / 3 )
fill_matrix_position(matrix,bit,x,y)
end
-- now bottom left
start_x = 1
start_y = size - 10
for i=1,#bitstring do
bit = string.sub(bitstring,i,i)
x = start_x + math.floor( (i - 1) / 3 )
y = start_y + math.fmod(i - 1,3)
fill_matrix_position(matrix,bit,x,y)
end
end
--- Now it's time to use the methods above to create a prefilled matrix for the given mask
local function prepare_matrix_with_mask( version,ec_level, mask )
local size
local tab_x = {}
size = version * 4 + 17
for i=1,size do
tab_x[i]={}
for j=1,size do
tab_x[i][j] = 0
end
end
add_position_detection_patterns(tab_x)
add_timing_pattern(tab_x)
add_version_information(tab_x,version)
-- black pixel above lower left position detection pattern
tab_x[9][size - 7] = 2
add_alignment_pattern(tab_x)
add_typeinfo_to_matrix(tab_x,ec_level, mask)
return tab_x
end
--- Finally we come to the place where we need to put the calculated data (remember step 3?) into the qr code.
--- We do this for each mask. BTW speaking of mask, this is what we find in the spec:
--- Mask Pattern Reference Condition
--- 000 (y + x) mod 2 = 0
--- 001 y mod 2 = 0
--- 010 x mod 3 = 0
--- 011 (y + x) mod 3 = 0
--- 100 ((y div 2) + (x div 3)) mod 2 = 0
--- 101 (y x) mod 2 + (y x) mod 3 = 0
--- 110 ((y x) mod 2 + (y x) mod 3) mod 2 = 0
--- 111 ((y x) mod 3 + (y+x) mod 2) mod 2 = 0
-- Return 1 (black) or -1 (blank) depending on the mask, value and position.
-- Parameter mask is 0-7 (-1 for 'no mask'). x and y are 1-based coordinates,
-- 1,1 = upper left. tonumber(value) must be 0 or 1.
local function get_pixel_with_mask( mask, x,y,value )
x = x - 1
y = y - 1
local invert = false
-- test purpose only:
if mask == -1 then -- luacheck: ignore
-- ignore, no masking applied
elseif mask == 0 then
if math.fmod(x + y,2) == 0 then invert = true end
elseif mask == 1 then
if math.fmod(y,2) == 0 then invert = true end
elseif mask == 2 then
if math.fmod(x,3) == 0 then invert = true end
elseif mask == 3 then
if math.fmod(x + y,3) == 0 then invert = true end
elseif mask == 4 then
if math.fmod(math.floor(y / 2) + math.floor(x / 3),2) == 0 then invert = true end
elseif mask == 5 then
if math.fmod(x * y,2) + math.fmod(x * y,3) == 0 then invert = true end
elseif mask == 6 then
if math.fmod(math.fmod(x * y,2) + math.fmod(x * y,3),2) == 0 then invert = true end
elseif mask == 7 then
if math.fmod(math.fmod(x * y,3) + math.fmod(x + y,2),2) == 0 then invert = true end
else
assert(false,"This can't happen (mask must be <= 7)")
end
if invert then
-- value = 1? -> -1, value = 0? -> 1
return 1 - 2 * tonumber(value)
else
-- value = 1? -> 1, value = 0? -> -1
return -1 + 2*tonumber(value)
end
end
-- We need up to 8 positions in the matrix. Only the last few bits may be less then 8.
-- The function returns a table of (up to) 8 entries with subtables where
-- the x coordinate is the first and the y coordinate is the second entry.
local function get_next_free_positions(matrix,x,y,dir,byte)
local ret = {}
local count = 1
local mode = "right"
while count <= #byte do
if mode == "right" and matrix[x][y] == 0 then
ret[#ret + 1] = {x,y}
mode = "left"
count = count + 1
elseif mode == "left" and matrix[x-1][y] == 0 then
ret[#ret + 1] = {x-1,y}
mode = "right"
count = count + 1
if dir == "up" then
y = y - 1
else
y = y + 1
end
elseif mode == "right" and matrix[x-1][y] == 0 then
ret[#ret + 1] = {x-1,y}
count = count + 1
if dir == "up" then
y = y - 1
else
y = y + 1
end
else
if dir == "up" then
y = y - 1
else
y = y + 1
end
end
if y < 1 or y > #matrix then
x = x - 2
-- don't overwrite the timing pattern
if x == 7 then x = 6 end
if dir == "up" then
dir = "down"
y = 1
else
dir = "up"
y = #matrix
end
end
end
return ret,x,y,dir
end
-- Add the data string (0's and 1's) to the matrix for the given mask.
local function add_data_to_matrix(matrix,data,mask)
local a = 1
local size = #matrix
local x,y,positions
local _x,_y,m
local dir = "up"
local byte_number = 0
x,y = size,size
print ("DATALENGTH",#data)
if (1==1) then
while (0 == 0) do
local byte = string.sub(data,byte_number*8+1,byte_number*8+8)
if byte == "" then
break
end
byte_number = byte_number + 1
-- if byte_number > 10 then
-- return
-- end
if (byte_number%20 == 0) and (CALCULATE_QRCODE_FOR_REPORT == 1) then
if file_open ~= nil then
start_timer(26,WAIT_26,0,1)
error2("qrcode_0003")
coroutine.yield()
else
notify("QRCode ADD_DATA_TO_MATRIX - byte_number: " .. tostring(byte_number) .. " WAIT26")
end
end
positions,x,y,dir = get_next_free_positions(matrix,x,y,dir,byte)
for i=1,#byte do -- das letzte Byte hat ggfs. keine 8 bits
_x = positions[i][1]
_y = positions[i][2]
m = get_pixel_with_mask(mask,_x,_y,string.sub(byte,i,i))
if debugging then
matrix[_x][_y] = m * (i + 10)
else
matrix[_x][_y] = m
end
end
end
end
if (1==0) then
string.gsub(data,".?.?.?.?.?.?.?.?",function ( byte ) -- originaler Code, aber sehr umstaendlich. Geht ueber gsub,
byte_number = byte_number + 1 -- welches einen "Cursor" zur Verfuegung stellen soll.
positions,x,y,dir = get_next_free_positions(matrix,x,y,dir,byte) -- Muss besser straightforward gemacht werden, s.o.
for i=1,#byte do -- i geht immer von 1 bis 8
_x = positions[i][1]
_y = positions[i][2]
m = get_pixel_with_mask(mask,_x,_y,string.sub(byte,i,i))
if debugging then
matrix[_x][_y] = m * (i + 10)
else
matrix[_x][_y] = m
end
end
end)
end
end
--- The total penalty of the matrix is the sum of four steps. The following steps are taken into account:
---
--- 1. Adjacent modules in row/column in same color
--- 1. Block of modules in same color
--- 1. 1:1:3:1:1 ratio (dark:light:dark:light:dark) pattern in row/column
--- 1. Proportion of dark modules in entire symbol
---
--- This all is done to avoid bad patterns in the code that prevent the scanner from
--- reading the code.
-- Return the penalty for the given matrix
local function calculate_penalty(matrix)
local penalty1, penalty2, penalty3 = 0,0,0
local size = #matrix
-- this is for penalty 4
local number_of_dark_cells = 0
-- 1: Adjacent modules in row/column in same color
-- --------------------------------------------
-- No. of modules = (5+i) -> 3 + i
local last_bit_blank -- < 0: blank, > 0: black
local is_blank
local number_of_consecutive_bits
-- first: vertical
for x=1,size do
number_of_consecutive_bits = 0
last_bit_blank = nil
for y = 1,size do
if matrix[x][y] > 0 then
-- small optimization: this is for penalty 4
number_of_dark_cells = number_of_dark_cells + 1
is_blank = false
else
is_blank = true
end
if last_bit_blank == is_blank then
number_of_consecutive_bits = number_of_consecutive_bits + 1
else
if number_of_consecutive_bits >= 5 then
penalty1 = penalty1 + number_of_consecutive_bits - 2
end
number_of_consecutive_bits = 1
end
last_bit_blank = is_blank
end
if number_of_consecutive_bits >= 5 then
penalty1 = penalty1 + number_of_consecutive_bits - 2
end
end
-- now horizontal
for y=1,size do
number_of_consecutive_bits = 0
last_bit_blank = nil
for x = 1,size do
is_blank = matrix[x][y] < 0
if last_bit_blank == is_blank then
number_of_consecutive_bits = number_of_consecutive_bits + 1
else
if number_of_consecutive_bits >= 5 then
penalty1 = penalty1 + number_of_consecutive_bits - 2
end
number_of_consecutive_bits = 1
end
last_bit_blank = is_blank
end
if number_of_consecutive_bits >= 5 then
penalty1 = penalty1 + number_of_consecutive_bits - 2
end
end
for x=1,size do
for y=1,size do
-- 2: Block of modules in same color
-- -----------------------------------
-- Blocksize = m × n -> 3 × (m-1) × (n-1)
if (y < size - 1) and ( x < size - 1) and ( (matrix[x][y] < 0 and matrix[x+1][y] < 0 and matrix[x][y+1] < 0 and matrix[x+1][y+1] < 0) or (matrix[x][y] > 0 and matrix[x+1][y] > 0 and matrix[x][y+1] > 0 and matrix[x+1][y+1] > 0) ) then
penalty2 = penalty2 + 3
end
-- 3: 1:1:3:1:1 ratio (dark:light:dark:light:dark) pattern in row/column
-- ------------------------------------------------------------------
-- Gives 40 points each
--
-- I have no idea why we need the extra 0000 on left or right side. The spec doesn't mention it,
-- other sources do mention it. This is heavily inspired by zxing.
if (y + 6 < size and
matrix[x][y] > 0 and
matrix[x][y + 1] < 0 and
matrix[x][y + 2] > 0 and
matrix[x][y + 3] > 0 and
matrix[x][y + 4] > 0 and
matrix[x][y + 5] < 0 and
matrix[x][y + 6] > 0 and
((y + 10 < size and
matrix[x][y + 7] < 0 and
matrix[x][y + 8] < 0 and
matrix[x][y + 9] < 0 and
matrix[x][y + 10] < 0) or
(y - 4 >= 1 and
matrix[x][y - 1] < 0 and
matrix[x][y - 2] < 0 and
matrix[x][y - 3] < 0 and
matrix[x][y - 4] < 0))) then penalty3 = penalty3 + 40 end
if (x + 6 <= size and
matrix[x][y] > 0 and
matrix[x + 1][y] < 0 and
matrix[x + 2][y] > 0 and
matrix[x + 3][y] > 0 and
matrix[x + 4][y] > 0 and
matrix[x + 5][y] < 0 and
matrix[x + 6][y] > 0 and
((x + 10 <= size and
matrix[x + 7][y] < 0 and
matrix[x + 8][y] < 0 and
matrix[x + 9][y] < 0 and
matrix[x + 10][y] < 0) or
(x - 4 >= 1 and
matrix[x - 1][y] < 0 and
matrix[x - 2][y] < 0 and
matrix[x - 3][y] < 0 and
matrix[x - 4][y] < 0))) then penalty3 = penalty3 + 40 end
end
end
-- 4: Proportion of dark modules in entire symbol
-- ----------------------------------------------
-- 50 ± (5 × k)% to 50 ± (5 × (k + 1))% -> 10 × k
local dark_ratio = number_of_dark_cells / ( size * size )
local penalty4 = math.floor(math.abs(dark_ratio * 100 - 50)) * 2
return penalty1 + penalty2 + penalty3 + penalty4
end
-- Create a matrix for the given parameters and calculate the penalty score.
-- Return both (matrix and penalty)
local function get_matrix_and_penalty(version,ec_level,data,mask)
if CALCULATE_QRCODE_FOR_REPORT == 1 then
if file_open ~= nil then
start_timer(26,WAIT_26,0,1)
error2("qrcode_0004")
coroutine.yield()
else
notify("QR - GET_MATRIX_AND_PENALTY, WAIT26")
end
end
local tab = prepare_matrix_with_mask(version,ec_level,mask)
add_data_to_matrix(tab,data,mask)
local penalty = calculate_penalty(tab)
return tab, penalty
-- return tab, 100
end
-- Return the matrix with the smallest penalty. To to this
-- we try out the matrix for all 8 masks and determine the
-- penalty (score) each.
local function get_matrix_with_lowest_penalty(version,ec_level,data)
local tab, penalty
local tab_min_penalty, min_penalty
-- try masks 0-7
tab_min_penalty, min_penalty = get_matrix_and_penalty(version,ec_level,data,0)
for i=1,7 do
tab, penalty = get_matrix_and_penalty(version,ec_level,data,i)
if penalty < min_penalty then
tab_min_penalty = tab
min_penalty = penalty
end
end
return tab_min_penalty
end
--- The main function. We connect everything together. Remember from above:
---
--- 1. Determine version, ec level and mode (=encoding) for codeword
--- 1. Encode data
--- 1. Arrange data and calculate error correction code
--- 1. Generate 8 matrices with different masks and calculate the penalty
--- 1. Return qrcode with least penalty
-- If ec_level or mode is given, use the ones for generating the qrcode. (mode is not implemented yet)
local function qrcode( str, ec_level, _mode ) -- luacheck: no unused args
local arranged_data, version, data_raw, mode, len_bitstring
version, ec_level, data_raw, mode, len_bitstring = get_version_eclevel_mode_bistringlength(str,ec_level)
data_raw = data_raw .. len_bitstring
if CALCULATE_QRCODE_FOR_REPORT == 1 then
if file_open ~= nil then
start_timer(26,WAIT_26,0,1)
error2("qrcode_0004")
coroutine.yield()
else
notify("QRCode 1, WAIT26")
end
end
data_raw = data_raw .. encode_data(str,mode)
if CALCULATE_QRCODE_FOR_REPORT == 1 then
if file_open ~= nil then
start_timer(26,WAIT_26,0,1)
error2("qrcode_0005")
coroutine.yield()
else
notify("QRCode 2, WAIT26")
end
end
data_raw = add_pad_data(version,ec_level,data_raw)
if CALCULATE_QRCODE_FOR_REPORT == 1 then
if file_open ~= nil then
start_timer(26,WAIT_26,0,1)
error2("qrcode_0006")
coroutine.yield()
else
notify("QRCode 3, WAIT26")
end
end
arranged_data = arrange_codewords_and_calculate_ec(version,ec_level,data_raw)
if CALCULATE_QRCODE_FOR_REPORT == 1 then
if file_open ~= nil then
start_timer(26,WAIT_26,0,1)
error2("qrcode_0007")
coroutine.yield()
else
notify("QRCode 4, WAIT26")
end
end
if math.fmod(#arranged_data,8) ~= 0 then
return false, string.format("Arranged data %% 8 != 0: data length = %d, mod 8 = %d",#arranged_data, math.fmod(#arranged_data,8))
end
arranged_data = arranged_data .. string.rep("0",remainder[version])
if CALCULATE_QRCODE_FOR_REPORT == 1 then
if file_open ~= nil then
start_timer(26,WAIT_26,0,1)
error2("qrcode_0008")
coroutine.yield()
else
notify("QRCode 5, WAIT26")
end
end
-- bis hierher ohne Abbruch
local tab = get_matrix_with_lowest_penalty(version,ec_level,arranged_data)
if (1==0) then
return true, {}
end
-- if CALCULATE_QRCODE_FOR_REPORT == 1 then
-- if file_open ~= nil then
-- start_timer(26,WAIT_26,0,1)
-- coroutine.yield()
-- else
-- notify("QRCode 6, WAIT26")
-- end
-- end
return true, tab
end
if testing then
return {
encode_string_numeric = encode_string_numeric,
encode_string_ascii = encode_string_ascii,
qrcode = qrcode,
binary = binary,
get_mode = get_mode,
get_length = get_length,
add_pad_data = add_pad_data,
get_generator_polynominal_adjusted = get_generator_polynominal_adjusted,
get_pixel_with_mask = get_pixel_with_mask,
get_version_eclevel_mode_bistringlength = get_version_eclevel_mode_bistringlength,
remainder = remainder,
--get_capacity_remainder = get_capacity_remainder,
arrange_codewords_and_calculate_ec = arrange_codewords_and_calculate_ec,
calculate_error_correction = calculate_error_correction,
convert_bitstring_to_bytes = convert_bitstring_to_bytes,
bit_xor = bit_xor,
}
end
return {
qrcode = qrcode
}
end
qrc = preload_1015()
--*************************************************************************
function preload_1016()
----------------------------------------------------------------------------------
--
-- LibCompress.lua
--
-- Authors: jjsheets and Galmok of European Stormrage (Horde)
-- Email : sheets.jeff@gmail.com and galmok@gmail.com
-- Licence: GPL version 2 (General Public License)
----------------------------------------------------------------------------------
-- Modified for use in OpenComputers
-- By: SpiritedDusty
local LibCompress = {}
-- local is faster than global
local type = type
local select = select
local next = next
local loadstring = loadstring
local setmetatable = setmetatable
local assert = assert
local table_insert = table.insert
local table_remove = table.remove
local table_concat = table.concat
local string_char = string.char
local string_byte = string.byte
local string_len = string.len
local string_sub = string.sub
local unpack = unpack
local pairs = pairs
local math_modf = math.modf
local bit_band = bit32_band
local bit_bor = bit32_bor
local bit_bxor = bit32_bxor
local bit_bnot = bit32_bnot
local bit_lshift = bit32_lshift
local bit_rshift = bit32_rshift
--------------------------------------------------------------------------------
-- Cleanup
local tables = {} -- tables that may be cleaned have to be kept here
local tables_to_clean = {} -- list of tables by name (string) that may be reset to {} after a timeout
local timeout = -1
-- tables that may be erased
local function cleanup()
for k,v in pairs(tables_to_clean) do
tables[k]={}
tables_to_clean[k]=nil
end
end
local function onUpdate(frame, elapsed)
timeout = timeout - elapsed
if timeout < 0 then
frame:Hide()
cleanup()
end
end
local function setCleanupTables(...)
timeout = 15 -- empty tables after 15 seconds
for i=1,select("#",...) do
tables_to_clean[(select(i, ...))] = true
end
end
----------------------------------------------------------------------
----------------------------------------------------------------------
--
-- compression algorithms
--------------------------------------------------------------------------------
-- LZW codec
-- implemented by sheets.jeff@gmail.com
-- encode is used to uniquely encode a number into a sequence of bytes that can be decoded using decode()
-- the bytes returned by this do not contain "\000"
local bytes = {}
local function encode(x)
for k = 1, #bytes do bytes[k] = nil end
local xmod
x, xmod = math_modf(x/255)
xmod = xmod * 255
bytes[#bytes + 1] = xmod
while x > 0 do
x, xmod = math_modf(x/255)
xmod = xmod * 255
bytes[#bytes + 1] = xmod
end
if #bytes == 1 and bytes[1] > 0 and bytes[1] < 250 then
return string_char(bytes[1])
else
for i = 1, #bytes do bytes[i] = bytes[i] + 1 end
return string_char(256 - #bytes, math.floor(table.unpack(bytes)))
end
end
--decode converts a unique character sequence into its equivalent number, from ss, beginning at the ith char.
-- returns the decoded number and the count of characters used in the decode process.
local function decode(ss,i)
i = i or 1
local a = string_byte(ss,i,i)
if a > 249 then
local r = 0
a = 256 - a
for n = i+a, i+1, -1 do
r = r * 255 + string_byte(ss,n,n) - 1
end
return r, a + 1
else
return a, 1
end
end
-- Compresses the given uncompressed string.
-- Unless the uncompressed string starts with "\002", this is guaranteed to return a string equal to or smaller than
-- the passed string.
-- the returned string will only contain "\000" characters in rare circumstances, and will contain none if the
-- source string has none.
local dict = {}
function LibCompress:CompressLZW(uncompressed)
if type(uncompressed) == "string" then
local dict_size = 256
for k in pairs(dict) do
dict[k] = nil
end
local result = {"\002"}
local w = ''
local ressize = 1
for i = 0, 255 do
dict[string_char(i)] = i
end
for i = 1, #uncompressed do
local c = uncompressed:sub(i,i)
local wc = w..c
if dict[wc] then
w = wc
else
dict[wc] = dict_size
dict_size = dict_size +1
local r = encode(dict[w])
ressize = ressize + #r
result[#result + 1] = r
w = c
end
end
if w then
local r = encode(dict[w])
ressize = ressize + #r
result[#result + 1] = r
end
if (#uncompressed+1) > ressize then
return table_concat(result)
else
return string_char(1)..uncompressed
end
else
return nil, "Can only compress strings"
end
end
-- if the passed string is a compressed string, this will decompress it and return the decompressed string.
-- Otherwise it return an error message
-- compressed strings are marked by beginning with "\002"
function LibCompress:DecompressLZW(compressed)
if type(compressed) == "string" then
if compressed:sub(1,1) ~= "\002" then
return nil, "Can only decompress LZW compressed data ("..tostring(compressed:sub(1,1))..")"
end
compressed = compressed:sub(2)
local dict_size = 256
for k in pairs(dict) do
dict[k] = nil
end
for i = 0, 255 do
dict[i] = string_char(i)
end
local result = {}
local t = 1
local delta, k
k, delta = decode(compressed,t)
t = t + delta
result[#result+1] = dict[k]
local w = dict[k]
local entry
while t <= #compressed do
k, delta = decode(compressed,t)
t = t + delta
entry = dict[k] or (w..w:sub(1,1))
result[#result+1] = entry
dict[dict_size] = w..entry:sub(1,1)
dict_size = dict_size + 1
w = entry
end
return table_concat(result)
else
return nil, "Can only uncompress strings"
end
end
--------------------------------------------------------------------------------
-- Huffman codec
-- implemented by Galmok of European Stormrage (Horde), galmok@gmail.com
local function addCode(tree, bcode,len)
if tree then
tree.bcode = bcode;
tree.blength = len;
if tree.c1 then
addCode(tree.c1, bit_bor(bcode, bit_lshift(1,len)), len+1)
end
if tree.c2 then
addCode(tree.c2, bcode, len+1)
end
end
end
local function escape_code(code, len)
local escaped_code = 0;
local b;
local l = 0;
for i = len-1, 0,- 1 do
b = bit_band( code, bit_lshift(1,i))==0 and 0 or 1
escaped_code = bit_lshift(escaped_code,1+b) + b
l = l + b;
end
return escaped_code, len+l
end
tables.Huffman_compressed = {}
tables.Huffman_large_compressed = {}
local compressed_size = 0
local remainder;
local remainder_length;
local function addBits(tbl, code, len)
remainder = remainder + bit_lshift(code, remainder_length)
remainder_length = len + remainder_length
if remainder_length > 32 then
return true -- Bits lost due to too long code-words.
end
while remainder_length>=8 do
compressed_size = compressed_size + 1
tbl[compressed_size] = string_char(bit_band(remainder, 255))
remainder = bit_rshift(remainder, 8)
remainder_length = remainder_length -8
end
end
-- word size for this huffman algorithm is 8 bits (1 byte). This means the best compression is representing 1 byte with 1 bit, i.e. compress to 0.125 of original size.
function LibCompress:CompressHuffman(uncompressed)
if not type(uncompressed)=="string" then
return nil, "Can only compress strings"
end
if #uncompressed == 0 then
return "\001"
end
-- make histogram
local hist = {}
local n = 0
-- dont have to use all datat to make the histogram
local uncompressed_size = string_len(uncompressed)
local c;
for i = 1, uncompressed_size do
c = string_byte(uncompressed, i)
hist[c] = (hist[c] or 0) + 1
end
--Start with as many leaves as there are symbols.
local leafs = {}
local leaf;
local symbols = {}
for symbol, weight in pairs(hist) do
leaf = { symbol=string_char(symbol), weight=weight };
symbols[symbol] = leaf;
table_insert(leafs, leaf)
end
--Enqueue all leaf nodes into the first queue (by probability in increasing order so that the least likely item is in the head of the queue).
table. sort(leafs, function(a,b) if a.weight<b.weight then return true elseif a.weight>b.weight then return false else return nil end end)
local nLeafs = #leafs
-- create tree
local huff = {}
--While there is more than one node in the queues:
local l,h, li, hi, leaf1, leaf2
local newNode;
while (#leafs+#huff > 1) do
-- Dequeue the two nodes with the lowest weight.
-- Dequeue first
if not next(huff) then
li, leaf1 = next(leafs)
table_remove(leafs, li)
elseif not next(leafs) then
hi, leaf1 = next(huff)
table_remove(huff, hi)
else
li, l = next(leafs);
hi, h = next(huff);
if l.weight<=h.weight then
leaf1 = l;
table_remove(leafs, li)
else
leaf1 = h;
table_remove(huff, hi)
end
end
-- Dequeue second
if not next(huff) then
li, leaf2 = next(leafs)
table_remove(leafs, li)
elseif not next(leafs) then
hi, leaf2 = next(huff)
table_remove(huff, hi)
else
li, l = next(leafs);
hi, h = next(huff);
if l.weight<=h.weight then
leaf2 = l;
table_remove(leafs, li)
else
leaf2 = h;
table_remove(huff, hi)
end
end
--Create a new internal node, with the two just-removed nodes as children (either node can be either child) and the sum of their weights as the new weight.
newNode = { c1 = leaf1, c2 = leaf2, weight = leaf1.weight+leaf2.weight }
table_insert(huff,newNode)
end
if #leafs>0 then
li, l = next(leafs)
table_insert(huff, l)
table_remove(leafs, li)
end
huff = huff[1];
-- assign codes to each symbol
-- c1 = "0", c2 = "1"
-- As a common convention, bit '0' represents following the left child and bit '1' represents following the right child.
-- c1 = left, c2 = right
addCode(huff,0,0);
if huff then
huff.bcode = 0
huff.blength = 1
end
-- READING
-- bitfield = 0
-- bitfield_len = 0
-- read byte1
-- bitfield = bitfield + bit_lshift(byte1, bitfield_len)
-- bitfield_len = bitfield_len + 8
-- read byte2
-- bitfield = bitfield + bit_lshift(byte2, bitfield_len)
-- bitfield_len = bitfield_len + 8
-- (use 5 bits)
-- word = bit_band( bitfield, bit_lshift(1,5)-1)
-- bitfield = bit_rshift( bitfield, 5)
-- bitfield_len = bitfield_len - 5
-- read byte3
-- bitfield = bitfield + bit_lshift(byte3, bitfield_len)
-- bitfield_len = bitfield_len + 8
-- WRITING
remainder = 0;
remainder_length = 0;
local compressed = tables.Huffman_compressed
--compressed_size = 0
-- first byte is version info. 0 = uncompressed, 1 = 8-bit word huffman compressed
compressed[1] = "\003"
-- Header: byte 0=#leafs, byte 1-3=size of uncompressed data
-- max 2^24 bytes
local l = string_len(uncompressed)
compressed[2] = string_char(bit_band(nLeafs-1, 255)) -- number of leafs
compressed[3] = string_char(bit_band(l, 255)) -- bit 0-7
compressed[4] = string_char(bit_band(bit_rshift(l, 8), 255)) -- bit 8-15
compressed[5] = string_char(bit_band(bit_rshift(l, 16), 255)) -- bit 16-23
compressed_size = 5
-- create symbol/code map
for symbol, leaf in pairs(symbols) do
addBits(compressed, symbol, 8);
if addBits(compressed, escape_code(leaf.bcode, leaf.blength)) then
-- code word too long. Needs new revision to be able to handle more than 32 bits
return string_char(0)..uncompressed
end
addBits(compressed, 3, 2);
end
-- create huffman code
local large_compressed = tables.Huffman_large_compressed
local large_compressed_size = 0
local ulimit
for i = 1, l, 200 do
ulimit = l<(i+199) and l or (i+199)
for sub_i = i, ulimit do
c = string_byte(uncompressed, sub_i)
addBits(compressed, symbols[c].bcode, symbols[c].blength)
end
large_compressed_size = large_compressed_size + 1
large_compressed[large_compressed_size] = table_concat(compressed, "", 1, compressed_size)
compressed_size = 0
end
-- add remainding bits (if any)
if remainder_length>0 then
large_compressed_size = large_compressed_size + 1
large_compressed[large_compressed_size] = string_char(remainder)
end
local compressed_string = table_concat(large_compressed, "", 1, large_compressed_size)
-- is compression worth it? If not, return uncompressed data.
if (#uncompressed+1) <= #compressed_string then
return "\001"..uncompressed
end
setCleanupTables("Huffman_compressed", "Huffman_large_compressed")
return compressed_string
end
-- lookuptable (cached between calls)
local lshiftMask = {}
setmetatable(lshiftMask, {
__index = function (t, k)
local v = bit_lshift(1, k)
rawset(t, k, v)
return v
end
})
-- lookuptable (cached between calls)
local lshiftMinusOneMask = {}
setmetatable(lshiftMinusOneMask, {
__index = function (t, k)
local v = bit_lshift(1, k)-1
rawset(t, k, v)
return v
end
})
local function getCode(bitfield, field_len)
if field_len>=2 then
local b;
local p = 0;
for i = 0, field_len-1 do
b = bit_band(bitfield, lshiftMask[i])
if not (p==0) and not (b == 0) then
-- found 2 bits set right after each other (stop bits)
return bit_band( bitfield, lshiftMinusOneMask[i-1]), i-1,
bit_rshift(bitfield, i+1), field_len-i-1
end
p = b
end
end
return nil
end
local function unescape_code(code, code_len)
local unescaped_code=0;
local b;
local l = 0;
local i = 0
while i < code_len do
b = bit_band( code, lshiftMask[i])
if not (b==0) then
unescaped_code = bit_bor(unescaped_code, lshiftMask[l])
i = i + 1
end
i = i + 1
l = l + 1
end
return unescaped_code, l
end
tables.Huffman_uncompressed = {}
tables.Huffman_large_uncompressed = {} -- will always be as big as the larges string ever decompressed. Bad, but clearing i every timetakes precious time.
function LibCompress:DecompressHuffman(compressed)
if not type(uncompressed)=="string" then
return nil, "Can only uncompress strings"
end
local compressed_size = #compressed
--decode header
local info_byte = string_byte(compressed)
-- is data compressed
if info_byte==1 then
return compressed:sub(2) --return uncompressed data
end
if not (info_byte==3) then
return nil, "Can only decompress Huffman compressed data ("..tostring(info_byte)..")"
end
local num_symbols = string_byte(string_sub(compressed, 2, 2)) + 1
local c0 = string_byte(string_sub(compressed, 3, 3))
local c1 = string_byte(string_sub(compressed, 4, 4))
local c2 = string_byte(string_sub(compressed, 5, 5))
local orig_size = c2*65536 + c1*256 + c0
if orig_size==0 then
return "";
end
-- decode code->symbal map
local bitfield = 0;
local bitfield_len = 0;
local map = {} -- only table not reused in Huffman decode.
setmetatable(map, {
__index = function (t, k)
local v = {}
rawset(t, k, v)
return v
end
})
local i = 6; -- byte 1-5 are header bytes
local c, cl;
local minCodeLen = 1000;
local maxCodeLen = 0;
local symbol, code, code_len, _bitfield, _bitfield_len;
local n = 0;
local state = 0; -- 0 = get symbol (8 bits), 1 = get code (varying bits, ends with 2 bits set)
while n<num_symbols do
if i>compressed_size then
return nil, "Cannot decode map"
end
c = string_byte(compressed, i)
bitfield = bit_bor(bitfield, bit_lshift(c, bitfield_len))
bitfield_len = bitfield_len + 8
if state == 0 then
symbol = bit_band(bitfield, 255)
bitfield = bit_rshift(bitfield, 8)
bitfield_len = bitfield_len -8
state = 1 -- search for code now
else
code, code_len, _bitfield, _bitfield_len = getCode(bitfield, bitfield_len)
if code then
bitfield, bitfield_len = _bitfield, _bitfield_len
c, cl = unescape_code(code, code_len)
map[cl][c]=string_char(symbol)
minCodeLen = cl<minCodeLen and cl or minCodeLen
maxCodeLen = cl>maxCodeLen and cl or maxCodeLen
--print("symbol: "..string_char(symbol).." code: "..tobinary(c, cl))
n = n + 1
state = 0 -- search for next symbol (if any)
end
end
i=i+1
end
-- dont create new subtables for entries not in the map. Waste of space.
-- But do return an empty table to prevent runtime errors. (instead of returning nil)
local mt = {}
setmetatable(map, {
__index = function (t, k)
return mt
end
})
local uncompressed = tables.Huffman_uncompressed
local large_uncompressed = tables.Huffman_large_uncompressed
local uncompressed_size = 0
local large_uncompressed_size = 0
local test_code
local test_code_len = minCodeLen;
local symbol;
local dec_size = 0;
compressed_size = compressed_size + 1
local temp_limit = 200; -- first limit of uncompressed data. large_uncompressed will hold strings of length 200
temp_limit = temp_limit > orig_size and orig_size or temp_limit
while true do
if test_code_len<=bitfield_len then
test_code=bit_band( bitfield, lshiftMinusOneMask[test_code_len])
symbol = map[test_code_len][test_code]
if symbol then
uncompressed_size = uncompressed_size + 1
uncompressed[uncompressed_size]=symbol
dec_size = dec_size + 1
if dec_size >= temp_limit then
if dec_size>=orig_size then -- checked here for speed reasons
break;
end
-- process compressed bytes in smaller chunks
large_uncompressed_size = large_uncompressed_size + 1
large_uncompressed[large_uncompressed_size] = table_concat(uncompressed, "", 1, uncompressed_size)
uncompressed_size = 0
temp_limit = temp_limit + 200 -- repeated chunk size is 200 uncompressed bytes
temp_limit = temp_limit > orig_size and orig_size or temp_limit
end
bitfield = bit_rshift(bitfield, test_code_len)
bitfield_len = bitfield_len - test_code_len
test_code_len = minCodeLen
else
test_code_len = test_code_len + 1
if test_code_len>maxCodeLen then
return nil, "Decompression error at "..tostring(i).."/"..tostring(#compressed)
end
end
else
c = string_byte(compressed, i)
bitfield = bitfield + bit_lshift(c or 0, bitfield_len)
bitfield_len = bitfield_len + 8
if i > compressed_size then
break;
end
i = i + 1
end
end
setCleanupTables("Huffman_uncompressed", "Huffman_large_uncompressed")
return table_concat(large_uncompressed, "", 1, large_uncompressed_size)..table_concat(uncompressed, "", 1, uncompressed_size)
end
--------------------------------------------------------------------------------
-- Generic codec interface
function LibCompress:DecompressUncompressed(data)
if type(data)~="string" then
return nil, "Can only handle strings"
end
if string.byte(data) ~= 1 then
return nil, "Can only handle uncompressed data"
end
return data:sub(2)
end
local compression_methods = {
[2] = LibCompress.CompressLZW,
[3] = LibCompress.CompressHuffman
}
local decompression_methods = {
[1] = LibCompress.DecompressUncompressed,
[2] = LibCompress.DecompressLZW,
[3] = LibCompress.DecompressHuffman
}
-- try all compression codecs and return best result
function LibCompress:Compress(data)
local method = next(compression_methods)
local result = compression_methods[method](self, data);
local n;
method = next(compression_methods, method)
while method do
n = compression_methods[method](self, data)
if #n < #result then
result = n
end
method = next(compression_methods, method)
end
return result
end
function LibCompress:Decompress(data)
local header_info = string.byte(data)
if decompression_methods[header_info] then
return decompression_methods[header_info](self, data)
else
return nil, "Unknown compression method ("..tostring(header_info)..")"
end
end
----------------------------------------------------------------------
----------------------------------------------------------------------
--
-- Encoding algorithms
--------------------------------------------------------------------------------
-- Prefix encoding algorithm
-- implemented by Galmok of European Stormrage (Horde), galmok@gmail.com
--[[
Howto: Encode and Decode:
3 functions are supplied, 2 of them are variants of the first. They return a table with functions to encode and decode text.
table, msg = LibCompress:GetEncodeTable(reservedChars, escapeChars, mapChars)
reservedChars: The characters in this string will not appear in the encoded data.
escapeChars: A string of characters used as escape-characters (don't supply more than needed). #escapeChars >= 1
mapChars: First characters in reservedChars maps to first characters in mapChars. (#mapChars <= #reservedChars)
return value:
table
if nil then msg holds an error message, otherwise use like this:
encoded_message = table:Encode(message)
message = table:Decode(encoded_message)
GetAddonEncodeTable: Sets up encoding for the addon channel (\000 is encoded)
GetChatEncodeTable: Sets up encoding for the chat channel (many bytes encoded, see the function for details)
Except for the mapped characters, all encoding will be with 1 escape character followed by 1 suffix, i.e. 2 bytes.
]]
-- to be able to match any requested byte value, the search string must be preprocessed
-- characters to escape with %:
-- ( ) . % + - * ? [ ] ^ $
-- "illegal" byte values:
-- 0 is replaces %z
local gsub_escape_table = {
['\000'] = "%z",
[('(')] = "%(",
[(')')] = "%)",
[('.')] = "%.",
[('%')] = "%%",
[('+')] = "%+",
[('-')] = "%-",
[('*')] = "%*",
[('?')] = "%?",
[('[')] = "%[",
[(']')] = "%]",
[('^')] = "%^",
[('$')] = "%$"
}
local function escape_for_gsub(str)
return str:gsub("([%z%(%)%.%%%+%-%*%?%[%]%^%$])", gsub_escape_table)
end
function LibCompress:GetEncodeTable(reservedChars, escapeChars, mapChars)
reservedChars = reservedChars or ""
escapeChars = escapeChars or ""
mapChars = mapChars or ""
-- select a default escape character
if escapeChars == "" then
return nil, "No escape characters supplied"
end
if #reservedChars < #mapChars then
return nil, "Number of reserved characters must be at least as many as the number of mapped chars"
end
if reservedChars == "" then
return nil, "No characters to encode"
end
-- list of characters that must be encoded
encodeBytes = reservedChars..escapeChars..mapChars
-- build list of bytes not available as a suffix to a prefix byte
local taken = {}
for i=1, strlen(encodeBytes) do
taken[string.sub(encodeBytes, i, i)] = true
end
-- allocate a table to holde encode/decode strings/functions
local codecTable = {}
-- the encoding can be a single gsub, but the decoding can require multiple gsubs
local decode_func_string = {}
local encode_search = {}
local encode_translate = {}
local decode_search = {}
local decode_translate = {}
local c,r,i,to,from
local escapeCharIndex = 0
-- map single byte to single byte
if #mapChars > 0 then
for i=1, #mapChars do
from = string.sub(reservedChars, i, i)
to = string.sub(mapChars, i, i)
encode_translate[from] = to
table.insert(encode_search, from)
decode_translate[to] = from
table.insert(decode_search, to)
end
codecTable["decode_search"..tostring(escapeCharIndex)] = "([".. escape_for_gsub(table.concat(decode_search)).."])"
codecTable["decode_translate"..tostring(escapeCharIndex)] = decode_translate
tinsert(decode_func_string, "str = str:gsub(self.decode_search"..tostring(escapeCharIndex)..", self.decode_translate"..tostring(escapeCharIndex)..");")
end
-- map single byte to double-byte
escapeCharIndex = escapeCharIndex +1
escapeChar = string.sub(escapeChars, escapeCharIndex, escapeCharIndex)
r = 0 -- suffix char value to the escapeChar
decode_search = {}
decode_translate = {}
for i = 1, strlen(encodeBytes) do
c = string.sub(encodeBytes, i, i)
if not encode_translate[c] then
-- this loop will update escapeChar and r
while r<256 and taken[string.char(r)] do
r=r+1
if r>255 then -- switch to next escapeChar
if escapeChar == "" then -- we are out of escape chars and we need more!
return nil, "Out of escape characters"
end
codecTable["decode_search"..tostring(escapeCharIndex)] = escape_for_gsub(escapeChar).."([".. escape_for_gsub(table.concat(decode_search)).."])"
codecTable["decode_translate"..tostring(escapeCharIndex)] = decode_translate
tinsert(decode_func_string, "str = str:gsub(self.decode_search"..tostring(escapeCharIndex)..", self.decode_translate"..tostring(escapeCharIndex)..");")
escapeCharIndex = escapeCharIndex + 1
escapeChar = string.sub(escapeChars, escapeCharIndex, escapeCharIndex)
r = 0
decode_search = {}
decode_translate = {}
end
end
encode_translate[c] = escapeChar..string.char(r)
table.insert(encode_search, c)
decode_translate[string.char(r)] = c
table.insert(decode_search, string.char(r))
r = r + 1
end
end
if r>0 then
codecTable["decode_search"..tostring(escapeCharIndex)] = escape_for_gsub(escapeChar).."([".. escape_for_gsub(table.concat(decode_search)).."])"
codecTable["decode_translate"..tostring(escapeCharIndex)] = decode_translate
tinsert(decode_func_string, "str = str:gsub(self.decode_search"..tostring(escapeCharIndex)..", self.decode_translate"..tostring(escapeCharIndex)..");")
end
-- change last line from "str = ...;" to "return ...;";
decode_func_string[#decode_func_string] = decode_func_string[#decode_func_string]:gsub("str = (.*);", "return %1;");
decode_func_string = "return function(self, str) "..table.concat(decode_func_string).." end"
encode_search = "([".. escape_for_gsub(table.concat(encode_search)).."])"
decode_search = escape_for_gsub(escapeChars).."([".. escape_for_gsub(table.concat(decode_search)).."])"
encode_func = assert(loadstring("return function(self, str) return str:gsub(self.encode_search, self.encode_translate); end"))()
decode_func = assert(loadstring(decode_func_string))()
codecTable.encode_search = encode_search
codecTable.encode_translate = encode_translate
codecTable.Encode = encode_func
codecTable.decode_search = decode_search
codecTable.decode_translate = decode_translate
codecTable.Decode = decode_func
codecTable.decode_func_string = decode_func_string -- to be deleted
return codecTable
end
-- Addons: Call this only once and reuse the returned table for all encodings/decodings.
function LibCompress:GetAddonEncodeTable(reservedChars, escapeChars, mapChars )
reservedChars = reservedChars or ""
escapeChars = escapeChars or ""
mapChars = mapChars or ""
-- Following byte values are not allowed:
-- \000
if escapeChars == "" then
escapeChars = "\001"
end
return self:GetEncodeTable( (reservedChars or "").."\000", escapeChars, mapChars)
end
-- Addons: Call this only once and reuse the returned table for all encodings/decodings.
function LibCompress:GetChatEncodeTable(reservedChars, escapeChars, mapChars)
reservedChars = reservedChars or ""
escapeChars = escapeChars or ""
mapChars = mapChars or ""
-- Following byte values are not allowed:
-- \000, s, S, \010, \013, \124, %
-- Because SendChatMessage will error if an UTF8 multibyte character is incomplete,
-- all character values above 127 have to be encoded to avoid this. This costs quite a bit of bandwidth (about 13-14%)
-- Also, because drunken status is unknown for the received, strings used with SendChatMessage should be terminated with
-- an identifying byte value, after which the server MAY add "...hic!" or as much as it can fit(!).
-- Pass the identifying byte as a reserved character to this function to ensure the encoding doesn't contain that value.
-- or use this: local message, match = arg1:gsub("^(.*)\029.-$", "%1")
-- arg1 is message from channel, \029 is the string terminator, but may be used in the encoded datastream as well. :-)
-- This encoding will expand data anywhere from:
-- 0% (average with pure ascii text)
-- 53.5% (average with random data valued zero to 255)
-- 100% (only encoding data that encodes to two bytes)
local i
local r={}
for i=128, 255 do
table.insert(r, string.char(i))
end
reservedChars = "sS\000\010\013\124%"..table.concat(r)..(reservedChars or "")
if escapeChars == "" then
escapeChars = "\029\031"
end
if mapChars == "" then
mapChars = "\015\020";
end
return self:GetEncodeTable(reservedChars, escapeChars, mapChars)
end
--------------------------------------------------------------------------------
-- 7 bit encoding algorithm
-- implemented by Galmok of European Stormrage (Horde), galmok@gmail.com
-- The encoded data holds values from 0 to 127 inclusive. Additional encoding may be necessary.
-- This algorithm isn't exactly fast and be used with care and consideration
tables.encode7bit = {}
function LibCompress:Encode7bit(str)
local remainder = 0;
local remainder_length = 0;
local tbl = tables.encode7bit
local encoded_size = 0
local l=#str
for i=1,l do
code = string.byte(str, i)
remainder = remainder + bit_lshift(code, remainder_length)
remainder_length = 8 + remainder_length
while remainder_length>=7 do
encoded_size = encoded_size + 1
tbl[encoded_size] = string_char(bit_band(remainder, 127))
remainder = bit_rshift(remainder, 7)
remainder_length = remainder_length -7
end
end
if remainder_length>0 then
encoded_size = encoded_size + 1
tbl[encoded_size] = string_char(remainder)
end
setCleanupTables("encode7bit")
return table.concat(tbl, "", 1, encoded_size)
end
tables.decode8bit = {}
function LibCompress:Decode7bit(str)
local bit8 = tables.decode8bit
local decoded_size = 0
local ch
local i=1
local bitfield_len=0
local bitfield=0
local l=#str
while true do
if bitfield_len >=8 then
decoded_size = decoded_size + 1
bit8[decoded_size] = string_char(bit.band(bitfield, 255))
bitfield = bit_rshift(bitfield, 8)
bitfield_len = bitfield_len - 8
end
ch=string_byte(str,i)
bitfield=bitfield+bit_lshift(ch or 0, bitfield_len)
bitfield_len = bitfield_len + 7
if i > l then
break
end
i=i+1
end
setCleanupTables("decode8bit")
return table.concat(bit8, "", 1, decoded_size)
end
----------------------------------------------------------------------
----------------------------------------------------------------------
--
-- Checksum/hash algorithms
--------------------------------------------------------------------------------
-- FCS16/32 checksum algorithms
-- converted from C by Galmok of European Stormrage (Horde), galmok@gmail.com
-- usage:
-- code = LibCompress:fcs16init()
-- code = LibCompress:fcs16update(code, data1)
-- code = LibCompress:fcs16update(code, data2)
-- code = LibCompress:fcs16update(code, data...)
-- code = LibCompress:fcs16final(code)
--
-- data = string
-- fcs16 provides a 16 bit checksum, fcs32 provides a 32 bit checksum.
--[[/* The following copyright notice concerns only the FCS hash algorithm
---------------------------------------------------------------------------
Copyright (c) 2003, Dominik Reichl <dominik.reichl@t-online.de>, Germany.
All rights reserved.
Distributed under the terms of the GNU General Public License v2.
This software is provided 'as is' with no explicit or implied warranties
in respect of its properties, including, but not limited to, correctness
and/or fitness for purpose.
---------------------------------------------------------------------------
*/]]
--// FCS-16 algorithm implemented as described in RFC 1331
local FCSINIT16 = 65535;
--// Fast 16 bit FCS lookup table
local fcs16tab = { [0]=0, 4489, 8978, 12955, 17956, 22445, 25910, 29887,
35912, 40385, 44890, 48851, 51820, 56293, 59774, 63735,
4225, 264, 13203, 8730, 22181, 18220, 30135, 25662,
40137, 36160, 49115, 44626, 56045, 52068, 63999, 59510,
8450, 12427, 528, 5017, 26406, 30383, 17460, 21949,
44362, 48323, 36440, 40913, 60270, 64231, 51324, 55797,
12675, 8202, 4753, 792, 30631, 26158, 21685, 17724,
48587, 44098, 40665, 36688, 64495, 60006, 55549, 51572,
16900, 21389, 24854, 28831, 1056, 5545, 10034, 14011,
52812, 57285, 60766, 64727, 34920, 39393, 43898, 47859,
21125, 17164, 29079, 24606, 5281, 1320, 14259, 9786,
57037, 53060, 64991, 60502, 39145, 35168, 48123, 43634,
25350, 29327, 16404, 20893, 9506, 13483, 1584, 6073,
61262, 65223, 52316, 56789, 43370, 47331, 35448, 39921,
29575, 25102, 20629, 16668, 13731, 9258, 5809, 1848,
65487, 60998, 56541, 52564, 47595, 43106, 39673, 35696,
33800, 38273, 42778, 46739, 49708, 54181, 57662, 61623,
2112, 6601, 11090, 15067, 20068, 24557, 28022, 31999,
38025, 34048, 47003, 42514, 53933, 49956, 61887, 57398,
6337, 2376, 15315, 10842, 24293, 20332, 32247, 27774,
42250, 46211, 34328, 38801, 58158, 62119, 49212, 53685,
10562, 14539, 2640, 7129, 28518, 32495, 19572, 24061,
46475, 41986, 38553, 34576, 62383, 57894, 53437, 49460,
14787, 10314, 6865, 2904, 32743, 28270, 23797, 19836,
50700, 55173, 58654, 62615, 32808, 37281, 41786, 45747,
19012, 23501, 26966, 30943, 3168, 7657, 12146, 16123,
54925, 50948, 62879, 58390, 37033, 33056, 46011, 41522,
23237, 19276, 31191, 26718, 7393, 3432, 16371, 11898,
59150, 63111, 50204, 54677, 41258, 45219, 33336, 37809,
27462, 31439, 18516, 23005, 11618, 15595, 3696, 8185,
63375, 58886, 54429, 50452, 45483, 40994, 37561, 33584,
31687, 27214, 22741, 18780, 15843, 11370, 7921, 3960 }
function LibCompress:fcs16init()
return FCSINIT16;
end
function LibCompress:fcs16update(uFcs16, pBuffer)
local i
local l=string_len(pBuffer)
for i = 1,l do
uFcs16 = bit_bxor(bit_rshift(uFcs16,8), fcs16tab[bit_band(bit_bxor(uFcs16, string_byte(pBuffer,i)), 255)])
end
return uFcs16
end
function LibCompress:fcs16final(uFcs16)
return bit_bxor(uFcs16,65535)
end
-- END OF FCS16
--[[/*
---------------------------------------------------------------------------
Copyright (c) 2003, Dominik Reichl <dominik.reichl@t-online.de>, Germany.
All rights reserved.
Distributed under the terms of the GNU General Public License v2.
This software is provided 'as is' with no explicit or implied warranties
in respect of its properties, including, but not limited to, correctness
and/or fitness for purpose.
---------------------------------------------------------------------------
*/]]
--// FCS-32 algorithm implemented as described in RFC 1331
local FCSINIT32 = -1
--// Fast 32 bit FCS lookup table
local fcs32tab = { [0]=0, 1996959894, -301047508, -1727442502, 124634137, 1886057615, -379345611, -1637575261,
249268274, 2044508324, -522852066, -1747789432, 162941995, 2125561021, -407360249, -1866523247,
498536548, 1789927666, -205950648, -2067906082, 450548861, 1843258603, -187386543, -2083289657,
325883990, 1684777152, -43845254, -1973040660, 335633487, 1661365465, -99664541, -1928851979,
997073096, 1281953886, -715111964, -1570279054, 1006888145, 1258607687, -770865667, -1526024853,
901097722, 1119000684, -608450090, -1396901568, 853044451, 1172266101, -589951537, -1412350631,
651767980, 1373503546, -925412992, -1076862698, 565507253, 1454621731, -809855591, -1195530993,
671266974, 1594198024, -972236366, -1324619484, 795835527, 1483230225, -1050600021, -1234817731,
1994146192, 31158534, -1731059524, -271249366, 1907459465, 112637215, -1614814043, -390540237,
2013776290, 251722036, -1777751922, -519137256, 2137656763, 141376813, -1855689577, -429695999,
1802195444, 476864866, -2056965928, -228458418, 1812370925, 453092731, -2113342271, -183516073,
1706088902, 314042704, -1950435094, -54949764, 1658658271, 366619977, -1932296973, -69972891,
1303535960, 984961486, -1547960204, -725929758, 1256170817, 1037604311, -1529756563, -740887301,
1131014506, 879679996, -1385723834, -631195440, 1141124467, 855842277, -1442165665, -586318647,
1342533948, 654459306, -1106571248, -921952122, 1466479909, 544179635, -1184443383, -832445281,
1591671054, 702138776, -1328506846, -942167884, 1504918807, 783551873, -1212326853, -1061524307,
-306674912, -1698712650, 62317068, 1957810842, -355121351, -1647151185, 81470997, 1943803523,
-480048366, -1805370492, 225274430, 2053790376, -468791541, -1828061283, 167816743, 2097651377,
-267414716, -2029476910, 503444072, 1762050814, -144550051, -2140837941, 426522225, 1852507879,
-19653770, -1982649376, 282753626, 1742555852, -105259153, -1900089351, 397917763, 1622183637,
-690576408, -1580100738, 953729732, 1340076626, -776247311, -1497606297, 1068828381, 1219638859,
-670225446, -1358292148, 906185462, 1090812512, -547295293, -1469587627, 829329135, 1181335161,
-882789492, -1134132454, 628085408, 1382605366, -871598187, -1156888829, 570562233, 1426400815,
-977650754, -1296233688, 733239954, 1555261956, -1026031705, -1244606671, 752459403, 1541320221,
-1687895376, -328994266, 1969922972, 40735498, -1677130071, -351390145, 1913087877, 83908371,
-1782625662, -491226604, 2075208622, 213261112, -1831694693, -438977011, 2094854071, 198958881,
-2032938284, -237706686, 1759359992, 534414190, -2118248755, -155638181, 1873836001, 414664567,
-2012718362, -15766928, 1711684554, 285281116, -1889165569, -127750551, 1634467795, 376229701,
-1609899400, -686959890, 1308918612, 956543938, -1486412191, -799009033, 1231636301, 1047427035,
-1362007478, -640263460, 1088359270, 936918000, -1447252397, -558129467, 1202900863, 817233897,
-1111625188, -893730166, 1404277552, 615818150, -1160759803, -841546093, 1423857449, 601450431,
-1285129682, -1000256840, 1567103746, 711928724, -1274298825, -1022587231, 1510334235, 755167117 }
function LibCompress:fcs32init()
return FCSINIT32;
end
function LibCompress:fcs32update(uFcs32, pBuffer)
local i
local l=string_len(pBuffer)
for i = 1,l do
uFcs32 = bit_bxor(bit_rshift(uFcs32,8), fcs32tab[bit_band(bit_bxor(uFcs32, string_byte(pBuffer,i)), 255)])
end
return uFcs32
end
function LibCompress:fcs32final(uFcs32)
return bit_bnot(uFcs32)
end
return LibCompress
end
zip = preload_1016()
--*************************************************************************
-- LIBRARIES_END
--*************************************************************************
-- MAIN_SECTION_BEGIN
--*************************************************************************
FA_OPEN_EXISTING = 0x00
FA_READ = 0x01
FA_WRITE = 0x02
FA_CREATE_NEW = 0x04
FA_CREATE_ALWAYS = 0x08
FA_OPEN_ALWAYS = 0x10
ladebalken = 0
sd_dir = '---'
sd_subdirs = {}
all_files = {}
add_write = FA_WRITE|FA_READ
over_write = FA_CREATE_ALWAYS|FA_WRITE
WriteOnceSize = 2048
sdcard_debug_txt = ''
lang_nr = 0
transl = {}
t_cache = {}
--*****************************************************************************
--core
function on_init ()
-- uart_setup(0,9600,8,1,1)
uart_set_timeout(20,10)
if file_open == nil then
print("PATCH")
local patch_file = "patch_"..string.format("%07u",tonumber(SERIALNUMBER))..".enc"
local file_handle = io.open(patch_file)
if file_handle ~= nil then
file_handle:close()
load_patch(patch_file)
end
end
app = App:new(0)
app.debug = 0
end
uart_free_protocol = 1
--**********************************************************************
function on_control_notify (sid,control_nr,val)
return app:on_control_notify(sid,control_nr,val)
end
--**********************************************************************
function on_screen_change (sid)
if app ~= nil then
-- print('ID',sid)
app:on_screen_change(sid)
end
end
--**********************************************************************
function on_uart_recv_data(packet)
app:on_uart_recv_data(packet,"comes from real hardware")
end
--**********************************************************************
function on_timer (tid)
if tid == 11 then
load_patch(PATCH_filename) -- einlesen der einzelnen parts des patchfiles
elseif tid == 12 then
load_parts() -- zusammenfuehren der einezlen parts und ausfuehren, so dass alle patch funktionen ausgefuehrt werden
else
return app:on_timer_pre(tid)
end
end
--**********************************************************************
function encode_general (text,alpha,alphb,nra,nrb) -- encode bit series to xx chars string
text = pad_up(text,string.sub(alpha,1,1),nra)
encoded_text = {}
while 0 == 0 do
local chunk_text = string.sub(text,1,nra)
text = string.sub(text,nra+1)
local chunk_nr = 0
for i=nra,1,-1 do
local char_aktuell = string.sub(chunk_text,i,i)
-- print("XXX",alpha,chunk_nr,char_aktuell)
-- if char_aktuell == ":" then
-- print(text)
-- end
chunk_nr = chunk_nr * #alpha + string.find(alpha,char_aktuell) - 1
end
for i=nrb,1,-1 do
local pos = math.floor(chunk_nr % #alphb) + 1
local new_char = string.sub(alphb,pos,pos)
table.insert(encoded_text,new_char)
chunk_nr = (chunk_nr + 0.001) // #alphb + 0.001
end
if #text == 0 then
break
end
end
text = pad_down( table.concat(encoded_text), string.sub(alphb,1,1) )
return(text)
end
--**********************************************************************
function mask_general (text,mask_char,mask_table)
local text2 = {}
local zaehler = 0
while 0 == 0 do
zaehler = zaehler + 1
local char_aktuell = string.sub(text,zaehler,zaehler)
if char_aktuell == "" then
break
end
local char_alt = mask_table[char_aktuell]
if char_alt ~= nil then
table.insert(text2,mask_char..char_alt)
else
table.insert(text2,char_aktuell)
end
end
return(table.concat(text2))
end
--**********************************************************************
function demask_general (text,mask_char,mask_table)
local text2 = {}
local zaehler = 0
while 0 == 0 do
zaehler = zaehler + 1
local char_aktuell = string.sub(text,zaehler,zaehler)
if char_aktuell == "" then
break
end
if char_aktuell == mask_char then
zaehler = zaehler + 1
char_aktuell = mask_table[string.sub(text,zaehler,zaehler)]
end
table.insert(text2,char_aktuell)
-- print("---",text2)
end
return(table.concat(text2))
end
--**********************************************************************
function pad_up (text,pad_char,pad_nr)
while #text % pad_nr > 0 do -- padding of entry text
text = text .. pad_char
end
return(text)
end
--**********************************************************************
function pad_down (text,pad_char)
while 0 == 0 do -- de-padding
if string.sub(text,#text,#text) == pad_char then
text = string.sub(text,1,#text-1)
else
break
end
end
return( text )
end
--**********************************************************************
function base27_encode (text)
local text1_crypt_bin = bin.stohex(text)
-- text1_crypt_bin = text1_crypt_bin .. "ff"
text1_crypt_bin = table.concat(bin.hextobin(text1_crypt_bin))
return( encode_general(text1_crypt_bin,CHAR02,CHAR27,19,4) )
end
--**********************************************************************
function base27_decode (text)
local text2 = encode_general(text1,CHAR27,CHAR02,4,19)
local text3 = {}
zaehler = 0
while 0 == 0 do
zaehler = zaehler + 1
if string.sub(text2,zaehler,zaehler) == "" then
break
end
table.insert(text3,tonumber(string.sub(text2,zaehler,zaehler)))
end
local text4 = bin.bintohex(text3)
local text5 = bin.hextos(text4)
return( text5 )
end
--**********************************************************************
function base62_encode (text)
local text1_crypt_bin = bin.stohex(text)
text1_crypt_bin = text1_crypt_bin .. "ff"
text1_crypt_bin = table.concat(bin.hextobin(text1_crypt_bin))
local text1 = encode_general(text1_crypt_bin,CHAR02,CHAR64,6,1)
local text2 = mask_general(text1,"0",MASK62FORWARD)
return(text2)
end
--**********************************************************************
function base62_decode (text)
local text1 = demask_general(text,"0",MASK62REVERSE)
local text2 = pad_up( encode_general(text1,CHAR64,CHAR02,1,6), "0",8 )
local text3 = {}
zaehler = 0
while 0 == 0 do
zaehler = zaehler + 1
if string.sub(text2,zaehler,zaehler) == "" then
break
end
table.insert(text3,tonumber(string.sub(text2,zaehler,zaehler)))
end
local text4 = bin.bintohex(text3)
text4 = string.sub(text4,1,#text4-2)
local text5 = bin.hextos(text4)
return(text5)
end
--**********************************************************************
function base32_encode (text)
local text1_crypt_bin = bin.stohex(text)
text1_crypt_bin = text1_crypt_bin .. "ff"
text1_crypt_bin = table.concat(bin.hextobin(text1_crypt_bin))
local text1 = encode_general(text1_crypt_bin,CHAR02,CHAR32,5,1)
return(text1)
end
--**********************************************************************
function base32_decode (text)
local text2 = pad_up( encode_general(text,CHAR32,CHAR02,1,5), "0",8 )
local text3 = {}
zaehler = 0
while 0 == 0 do
zaehler = zaehler + 1
if string.sub(text2,zaehler,zaehler) == "" then
break
end
table.insert(text3,tonumber(string.sub(text2,zaehler,zaehler)))
end
local text4 = bin.bintohex(text3)
text4 = string.sub(text4,1,#text4-2)
local text5 = bin.hextos(text4)
return(text5)
end
--**********************************************************************
function t (expression,nr)
if nr == nil then
nr = lang_nr
end
-- local erg = t_cache[tonumber(nr) .. expression]
-- if erg ~= nil then
-- return erg
-- end
-- print("EXPR",expression)
if expression == "Parkzeit" then
if PRUEFCODE % 10 > 0 then
expression = "Anzahl Tickets"
end
end
local x = string.find(translation_table,"\n[^\n]-;"..expression..";")
if x == nil then
if translation_table == "" then
return("")
else
return expression
end
end
local ttab1 = string.sub(translation_table,x+1)
x = string.find(ttab1,"\n")
local ttab2 = string.sub(ttab1,1,x-1)
x = ";.-"
local ttab3 = string.match(ttab2,x:rep(nr)..";(.-);")
if ttab3 == nil or ttab3 == "-" then
erg = ""
else
erg = ttab3
end
-- t_cache[tonumber(nr) .. expression] = erg
return(erg)
end
--*************************************************************************
function text_to_numbers (text)
local number_text = ""
zaehler = 0
while (0 == 0) do
zaehler = zaehler + 1
sign = string.sub(text,zaehler,zaehler)
if sign == "" then
break
end
number_text = number_text .. "," .. tostring(string.byte(sign))
end
return(number_text)
end
--*************************************************************************
function numbers_to_text (numbers)
local text = ""
for zeile in string.gmatch(numbers,'[^,]+') do
text = text .. string.char(tonumber(zeile))
end
return(text)
end
--**********************************************************************
function replace_diacritic_letters (text,dia_mode)
local text1 = text_to_numbers(text)
if dia_mode == "DE" then
for key,val in pairs(DIACRITIC_REPLACE_DE) do
if (dia_mode == nil) then
break
end
local key1 = text_to_numbers(key)
local val1 = text_to_numbers(val)
text1 = string.gsub(text1,key1,val1)
end
end
for key,val in pairs(DIACRITIC_REPLACE) do
local key1 = text_to_numbers(key)
local val1 = text_to_numbers(val)
text1 = string.gsub(text1,key1,val1)
end
text = numbers_to_text(text1)
return(text)
end
--**********************************************************************
function my_debug_print(debugInfo)
--print(debugInfo)
--sdcard_debug_txt=sdcard_debug_txt..debugInfo
--debugInfo=get_text(2,8)..debugInfo
--set_text(2,8,debugInfo)
--set_text(3,8,debugInfo)
-- if open_debug_uatr == 1
-- then
-- print(debugInfo)
-- end
end
--**********************************************************************
--Write data to the end of the file
--@file:file path
--@data:The type of '@data' only be [string] or [byte array]
--@open_mode:open file mode
--**********************************************************************
function my_getdataLen_Type(data)
my_debug_print('---------- my_getdataLen_Type ----------')
local datalen = -1
--获取数据类型
local data_type = type(data)
--计算数据长度
if data_type == 'string'
then
datalen = string.len(data)
elseif data_type == 'table'
then
datalen = #(data)
end
my_debug_print('Lua_debug data Type and Len: '..data_type..' / '..datalen)
return datalen,data_type
end
--**********************************************************************
function my_write_filedata1(file0, file, data, open_mode)
local erg = ""
if file0 == nil or file_open ~= nil then
erg = my_write_filedata(file,data,open_mode)
else
f = io.open(file0,'w')
f:write(data)
f:close()
end
end
--**********************************************************************
--core
function my_read_filedata1(file0, file)
if file0 == nil or file_open ~= nil then
return( my_read_filedata(file,data,open_mode) )
else
local file1 = io.open(file0)
local file2 = ""
while 0 == 0 do
if file1 == nil then
break
end
local line1 = file1:read()
if line1 == nil then
file1:close()
break
end
file2 = file2 .. line1 .. "\n"
end
return(file2)
end
end
--**********************************************************************
function my_write_filedata(file, data, open_mode)
my_debug_print('---------- my_write_filedata ----------')
my_debug_print('Lua_debug: file -> '..file..' / data -> '..type(data)..' / open_mode -> '..open_mode)
--local sc_prompt=1
local count = 0
local write_cnt = 0
local seek_ops = 0
local all_byte = 0
--获取待写入数据的数据类型和长度
local wrire_len, data_type = my_getdataLen_Type(data)
local write_byte_Tb = {}
local open_state = file_open(file, open_mode)
local erg = ""
if open_state == true
then
--获取当前文件大小,仅在追加文件末尾写入执行
if open_mode == add_write
then
all_byte = file_size()
end
if wrire_len > 0
then
--计算'@data'要写多少次
write_cnt = math.modf(wrire_len / WriteOnceSize)
if wrire_len % WriteOnceSize > 0
then
write_cnt = write_cnt + 1
end
my_debug_print('Lua_debug: need write allcnt -> '..write_cnt)
for i = 1, write_cnt
do
--复位写字节数组
write_byte_Tb = {}
--计算写的位置
seek_ops = (i - 1) * WriteOnceSize +all_byte
file_seek(seek_ops)
--文件偏移失败
if offst_result == false
then
print('When writing the file, an offset error occurred. please try again! ! !')
break
end
my_debug_print('Lua_debug: cur seek_ops -> '..seek_ops)
--计算本次写的个数
count = WriteOnceSize
if i == write_cnt
then
if wrire_len % WriteOnceSize > 0
then
count = wrire_len % WriteOnceSize
end
end
my_debug_print('Lua_debug: cur write -> '..write_cnt..'th / wrire count '..count)
--填充写入flash的字节数组
for j = 1, count
do
if data_type == 'string'
then
--字符串类型,将每个字符转换为字节数组
write_byte_Tb[j - 1] = tonumber(string.byte(data, ((i - 1) * WriteOnceSize + j), ((i - 1) * WriteOnceSize + j)))
elseif data_type == 'table'
then
--数组类型,字节赋值
write_byte_Tb[j - 1] = data[((i - 1) * WriteOnceSize + j)]
end
end
local IswriteOK = file_write(write_byte_Tb)
if IswriteOK == false
then
i = i - 1
end
end
end
else
print('The file does not exist, please check the contents of the SD card! ! !')
erg = "ERROR"
end
--关闭文件
file_close()
return(erg)
end
--**********************************************************************
--core
function my_read_filedata(file)
my_debug_print('---------- my_read_filedata ----------')
my_debug_print('Lua_debug: file -> '..file)
local count = 0
local read_cnt = 0
local offset = 0
local all_byte = 0
local read_byte_Tb = {}
local read_char_Tb = {}
local read_str = ''
local open_state = file_open(file, FA_READ)
if open_state == true then
--获取当前文件大小
all_byte = file_size()
if all_byte > 0 then
read_cnt = math.modf(all_byte/WriteOnceSize)
if all_byte % WriteOnceSize > 0
then
read_cnt = read_cnt + 1
end
my_debug_print('Lua_debug: need read allcnt -> '..read_cnt)
for i = 1, read_cnt
do
--复位读字节数组
read_byte_Tb = {}
read_char_Tb = {}
--计算读取的偏移位置
offset = (i - 1) * WriteOnceSize
local offst_result = file_seek(offset)
--文件偏移失败
if offst_result == false
then
print('When reading the file, an offset error occurred. please try again! ! !')
break
end
my_debug_print('Lua_debug: cur offset -> '.. offset)
--计算本次读的个数
count = WriteOnceSize
if i == read_cnt
then
if all_byte % WriteOnceSize > 0
then
count = all_byte % WriteOnceSize
end
end
my_debug_print('Lua_debug: cur read -> '..i..'th / wrire count '..count)
--读取字节转换为字符并拼接成字符串
read_byte_Tb = file_read(count)
if #(read_byte_Tb) > 0
then
for j = 0, #(read_byte_Tb)
do
read_char_Tb[j + 1] = string.char(read_byte_Tb[j])
end
read_str = read_str..table.concat(read_char_Tb)
elseif read_byte_Tb ==nil
then
print(' File read error. please try again! ! !')
break;
end
end
end
file_close()
end
return read_str
end
--**********************************************************************
--core
function on_sd_inserted (dir)
if DISABLED == 0 then
sd_dir = dir
list_dir(sd_dir)
end
end
--**********************************************************************
function print_symbols ()
for symbol,value in pairs(_G) do
print(symbol,value)
feed_the_dog()
end
end
--**********************************************************************
function error1 (text)
local atime = App.time('')
local dtime = string.sub(App.date('',"%y%m%d",atime),3,8) .. "_" .. App.date('',"%H%M%S",atime)
local text1 = text -- .. "\n" .. debug.traceback()
my_write_filedata1("debug3.txt",sd_dir..'/'.."debug3.txt",dtime .. ": " .. text1 .."\n",add_write)
end
--**********************************************************************
function error2 (text)
if SERIALNUMBER == "9999999" then
local atime = App.time('')
local dtime = string.sub(App.date('',"%y%m%d",atime),3,8) .. "_" .. App.date('',"%H%M%S",atime)
local text1 = text -- .. "\n" .. debug.traceback()
my_write_filedata1("debug5.txt",sd_dir..'/'.."debug5.txt",dtime .. ": " .. text1 .."\n",add_write)
end
return(0)
end
--**********************************************************************
function error2_shift ()
local debug5 = my_read_filedata1('debug5.txt',sd_dir..'/debug5.txt')
if debug5 == nil then
debug5 = ""
end
local atime = App.time()
local dtime = string.sub(App.date('',"%y%m%d",atime),3,8) .. "_" .. App.date('',"%H%M%S",atime)
my_write_filedata1("debug5__" .. dtime .. ".txt",sd_dir..'/'.."debug5__" .. dtime .. ".txt",debug5.."\n",over_write)
my_write_filedata1("debug5.txt",sd_dir..'/'.."debug5.txt","DEBUG5\n",over_write)
end
--**********************************************************************
function normal_signs (text)
local text1 = string.gsub(text,"[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,;:-=+_.#)(!] \r\n","")
return("")
-- return(text1)
end
--**********************************************************************
function feed_the_dog ()
if file_open ~= nil then
feed_dog()
end
end
--**********************************************************************
function on_list_dir (path,filename)
feed_the_dog()
if path == sd_dir then
table.insert(sd_subdirs,filename)
-- list_dir(path.."/"..filename)
if string.sub(filename,1,5) == 'patch' then
start_timer(11,500,0,1)
PATCH_filename = filename
ladebalken = 5
set_text(25,5,ladebalken)
end
end
-- table.insert(all_files,path.."/"..filename)
end
--**********************************************************************
--core
function load_patch (filename)
stop_timer(10)
ladebalken = ladebalken + 5
set_text(25,1,ladebalken)
patch_file = filename
local patch_enc = ""
PATCH_plaintext = ''
PATCH_parts = 0
PATCH_zaehler = 0
PATCH_fortschritt = 1
PATCH_nr = 0
PATCH_zeilen = {}
feed_the_dog()
if file_open == nil then
local file1 = io.open(filename)
print("LOAD PARTS",filename)
local file2 = ""
while 0 == 0 do
if file1 == nil then
break
end
local line1 = file1:read()
if line1 == nil then
file1:close()
break
end
file2 = file2 .. line1 .. "\n"
end
patch_enc = file2
else
-- set_text(14,11,sd_dir..'/'..filename)
patch_enc = my_read_filedata(sd_dir..'/'..filename)
end
feed_the_dog()
for zeile in string.gmatch(patch_enc,'[^\n]+') do
table.insert(PATCH_zeilen,zeile)
end
PATCH_offset = ladebalken or 0
PATCH_rest = 23
start_timer(12,1,0,1)
end
--*********************************************************************
--core
function load_parts ()
stop_timer(10)
feed_the_dog()
local ciph = ''
PATCH_nr = PATCH_nr + 1
while PATCH_zaehler < #PATCH_zeilen do
feed_the_dog()
PATCH_zaehler = PATCH_zaehler + 1
local zeile = PATCH_zeilen[PATCH_zaehler]
if string.sub(zeile,1,2) == "--" then
if PATCH_parts == 0 then
PATCH_parts = PATCH_parts + tonumber(string.sub(zeile,3))
else
local chunk = ""
if #ciph > 20 then
chunk = App.decrypt('',sec_key1,trim(ciph))
end
feed_the_dog()
PATCH_plaintext = PATCH_plaintext .. chunk
ciph = ''
ladebalken = math.floor((100-PATCH_offset-PATCH_rest)*PATCH_zaehler/#PATCH_zeilen) + PATCH_offset
set_value(25,1,ladebalken)
break
end
else
ciph = ciph .. zeile .. "\n"
end
end
if PATCH_zaehler == #PATCH_zeilen and #PATCH_plaintext > 20 then
-- set_text(14,11,string.sub(PATCH_plaintext,10,25))
feed_the_dog()
patch_call = load(PATCH_plaintext)
feed_the_dog()
GIT_COMMIT0 = GIT_COMMIT -- der git commit der Basis Version ist nun in GIT_COMMIT0
local SAVE_sd_dir = sd_dir -- wird durch den nachfolgenden patch_call ggfs. ueberschrieben
local SAVE_sd_subdirs = sd_subdirs
patch_call()
PATCH_plaintext = ""
feed_the_dog()
sd_dir = SAVE_sd_dir
sd_subdirs = SAVE_sd_subdirs
ladebalken = 77
start_timer(10,1,0,1)
else
start_timer(12,100,0,1)
end
end
--*********************************************************************
function trim (text)
return (text:gsub('^%s*(.-)%s*$', '%1'))
end
--*************************************************************************
-- CLASSES
--*************************************************************************
--private
App = {}
App.__index = App
--*************************************************************************
--system
function App:new (simulation)
local self = setmetatable({},App)
self.simulation = simulation
if self.simulation ~= 1 then
set_text(25,1,0)
change_screen(25) -- warte auf das Laden der Hotfixes
-- ladebalken = 0
ladebalken_interval = 8 -- x 100 Millisekunden Boot time, weil 100 Runden
start_timer(10,1,0,1)
else
if App.init ~= nil then
self:init()
end
end
local xx = 1
return self
end
--**********************************************************************
function App.on_timer_pre (self,tid)
if tid == 10 then -- Startverzoegerung, damit patches geladen werden koennen
if ladebalken > 99 then
if App.init ~= nil then
feed_the_dog()
self:init()
end
else
ladebalken = ladebalken + 1
set_value(25,1,ladebalken)
feed_the_dog()
start_timer(10,ladebalken_interval,0,1)
end
-- elseif tid == 12 then
-- set_value(25,1,PATCH_fortschritt)
else
return self:on_timer(tid)
end
end
--**********************************************************************
function App.decrypt (self,ckey,text)
if text == nil then
return ''
end
local text1 = string.gsub(text, "\r","")
text1 = string.gsub(text1,"\n","")
local text2 = bin.hextos(text1)
if #text2 < 16 then
return(text)
end
local erg = ''
if #ckey == 32 then
local ckey1 = bin.hextos(ckey)
erg = rc4.decrypt(ckey1,text2)
elseif #ckey == 64 then
local ckey1 = bin.hextos(ckey)
erg = salsa20.decrypt(ckey1,1,bin.hextos('1234567890abcdef'),text2)
else
local ckey1 = bin.hextos( string.sub(ckey,1,64) )
local ckey2 = bin.hextos( string.sub(ckey,65) )
-- print(ckey1,ckey2)
erg = crypto.box_open(text2,string.sub(SERIALNUMBER..'00000000000000000000',1,24),
ckey1,ckey2)
-- print(1234)
end
if erg == '' then
return(text)
end
return erg
end
--**********************************************************************
function App.crypt (self,ckey,text,zeilenlaenge)
if text == nil then
return ''
end
-- if #text < 32 then
-- return text
-- end
local erg = ''
if #ckey == 32 then
local ckey1 = bin.hextos(ckey)
error2("32crypt")
erg = rc4.encrypt(ckey1,text)
elseif #ckey == 64 then
local ckey1 = bin.hextos(ckey)
error2("64crypt")
erg = salsa20.encrypt(ckey1,1,bin.hextos('1234567890abcdef'),text)
else
error2("96crypt")
local ckey1 = bin.hextos( string.sub(ckey,1,64) )
local ckey2 = bin.hextos( string.sub(ckey,65) )
erg = crypto.box(text,string.sub(SERIALNUMBER..'00000000000000000000',1,24),
ckey1,ckey2)
end
-- if zeilenlaenge ~= nil then -- dieser Code funktioniert nicht! Der Loader bleibt haengen!
-- if zeilenlaenge > 0 then
erg = bin.stohex(erg)
-- else
-- erg = base27_encode(erg)
-- zeilenlaenge = -zeilenlaenge
-- end
-- end
erg1 = ""
while zeilenlaenge ~= nil do
-- error2("99crypt"..tostring(zeilenlaenge).." "..tostring(#erg))
erg1 = erg1 .. string.sub(erg,1,zeilenlaenge) .. "\n"
erg = string.sub(erg,zeilenlaenge+1)
if #erg == 0 then
return(erg1.."\n")
end
end
return erg
end
--**********************************************************************
function App.save_settings (self)
local text = ''
text = text .. "T1:" .. self.price_per_hour[1]:read() .. "," .. self.min_sum[1]:read() .. "," .. self.max_park_hour[1]:read() .. "," ..
self.max_park_minute[1]:read() .. self.tagesticket[1]:read() .. "," .. self.pay_mode[1]:read() .. "," .. self.tax_vat[1]:read() .. "\n"
text = text .. "T2:" .. self.price_per_hour[2]:read() .. "," .. self.min_sum[2]:read() .. "," .. self.max_park_hour[2]:read() .. "," ..
self.max_park_minute[2]:read() .. self.tagesticket[2]:read() .. "," .. self.pay_mode[2]:read() .. "," .. self.tax_vat[2]:read() .. "\n"
text = text .. "CF:" .. self.max_zeilen:read() .. "," .. self.report_number:read() .. "\n"
text = text .. "L1:" .. self.location1() .. "\n"
text = text .. "L2:" .. self.location2() .. "\n"
text = text .. "L3:" .. self.location3() .. "\n"
text = text .. "LANG:" .. self.lang_description1() .. "," .. self.lang_description2() .. "," .. self.lang_description3() .. "\n"
text = text .. "PIN:" .. self.pin_definitions() .. "\n"
text = text .. "TARF:"
for i = 1,10,1 do
text = text .. self.tarif_par[1][i]:read() .. "," .. self.tarif_par[2][i]:read() .. ","
.. self.tarif_par[3][i]:read() .. "," .. self.tarif_par[4][i]:read() .. ","
end
text = text .. "\n"
return(text)
end
--**********************************************************************
function App.init (self)
if self.simulation == 1 then
local statf = io.open('/proc/self/stat')
math.randomseed(self:time()+os.clock()+tonumber(string.sub(statf:read(),1,4)))
statf:close()
else
init_time = self:time()
math.randomseed(init_time)
self.comment = 'START_COMMENT '
self.counter = nil
self:no_simulation()
end
self.sign_idx = string.format("%4u",self:time())
CHECK_SIGN = "sign_" .. self.sign_idx .. ".txt"
feed_the_dog()
self.debug3 = my_read_filedata1('debug3.txt',sd_dir..'/debug3.txt')
if self.debug3 == nil then
self.debug3 = ""
end
-- self.debug3 = "vvvv"
if #self.debug3 > 10 then
local atime = self:time()
local dtime = string.sub(self:date("%y%m%d",atime),3,8) .. "_" .. self:date("%H%M%S",atime)
my_write_filedata1("debug3__" .. dtime .. ".txt",sd_dir..'/'.."debug3__" .. dtime .. ".txt",self.debug3.."\n",over_write)
end
feed_the_dog()
my_write_filedata1("debug3.txt",sd_dir..'/'.."debug3.txt","DEBUG\n",over_write)
PRINT_BF_EXIST = string.sub( VOUCHER_SIGNA_TABLE, VOUCHER_M*121, VOUCHER_M*121+5 )
feed_the_dog()
self:init1()
end
--**********************************************************************
function App.init1 (self) local s, r = xpcall(App.p_init1,function(e) error1(e) end,self) return(r) end
--**********************************************************************
function App.p_init1 (self)
self.uart_sperre = 1
self.field_sim = {}
self.screens = {}
self.fields = {}
self.cache_langcode_to_nr = {}
XFILES = {}
for i,file in ipairs(sd_subdirs) do
if string.sub(file,1,1) == "x" or string.sub(file,1,1) == "X" then
table.insert(XFILES,file) -- Unterverzeichnisse mit verschluesselten Dateien
list_dir(file)
end
end
feed_the_dog()
self.press_release = 1 -- 0: on Press, 2: on Release mit long press, 1: on Press mit long (zwei Ereignisse)
-- 11: if 1 occurs on a screen, set it to 2 for the next times.
self.machine_is_running = 0
if LANGUAGES == nil then
LANGUAGES = {"DE","EN","FR"}
FLAGS = {"DE","EN","FR"}
end
self.main = Screen:new(self, 0)
if file_open == nil then
self.standby = Screen:new(self,1)
else
self.standby = Screen:new(self,19)
end
self.standby.pr_re = 2
self.lang_layout3 = Screen:new(self, 2, { 171, 270, 271, } )
self.coin = Screen:new(self, 3, { 171, 271, 270, 267, 277, 278, 279, 201, 204, } )
self.print_msg = Screen:new(self, 4, { } )
self.admin = Screen:new(self, 5, { 217, 202, 201, 212, 213, 205, 299, 204, } )
self.adm_tarif = Screen:new(self, 6, { 203, 208, 201, 204, 207, 208, 211, 215, 216, 217, 218, 299, 206, 219, 220, 221, 222, 233, 240, } )
self.adm_ext = Screen:new(self, 7, { 204, 299, 201, 212, 203, 208, } )
self.adm_basic = Screen:new(self, 8, { 202, 203, 204, 207, 206, 299, 205, 211, 215, } )
self.adm_proto = Screen:new(self, 9, { 213, 205, 299, 203, 202, 204, } )
self.not_ready = Screen:new(self,10, { 270, } ) -- Gerät ist nicht betriebsbereit Screen
self.adm_lang_sel = Screen:new(self,11, { 280, 299, } )
self.pin = Screen:new(self,12, { 280, 202, } )
self.invalid_pin = Screen:new(self,13, { } )
self.ticketslot = Screen:new(self,14, { 280, 207, 217, } )
self.lang_layout2 = Screen:new(self,15, { 171, 270, 271, } )
self.lang_layout1 = Screen:new(self,16, { 171, 270, 271, } )
self.pin_admin = Screen:new(self,18, { 262, 299, } )
self.pin_template = Screen:new(self,20, { } )
self.adm_pin_new = Screen:new(self,21, { 201, 204, 299, 270, 201, 217, 205, } )
self.adm_tarif2 = Screen:new(self,22, { 203, 208, 201, 204, 207, 208, 211, 215, 216, 217, 218, 299, 206, 219, 220, 221, 222, 233, 240, } )
self.adm_device = Screen:new(self,23, { 220, 217, 218, 203, 202, 204, 205, 206, 207, 299, 201, } )
self.adm_soft_reset = Screen:new(self,24, { } )
self.start_screen = Screen:new(self,25, { } )
self.no_sdcard = Screen:new(self,26, { 270, } )
self.qrcode = Screen:new(self,27, { 3, 5, 299 } )
self.adm_tarif_def = Screen:new(self,28, { 204, 215, 216, 217, 218, 299 } )
self.adm_tarif_def2 = Screen:new(self,29, { 204, 215, 216, 217, 218, 299 } )
self.debug1 = Field:new(self,'')
self.debug1:add_field(self.standby,200) -- das ist der gruene Debug-Balken, der aktiviert werden kann
self.debug1:add_field(self.lang_layout3,200)
self.debug1:add_field(self.coin,200)
self.debug1:add_field(self.print_msg,200)
self.debug1:add_field(self.admin,200)
self.debug1:add_field(self.adm_tarif,200)
self.debug1:add_field(self.adm_ext,200)
self.debug1:add_field(self.adm_basic,200)
self.debug1:add_field(self.adm_proto,200)
self.debug1:add_field(self.not_ready,200)
self.debug1:add_field(self.adm_lang_sel,200)
self.debug1:add_field(self.pin,200)
self.debug1:add_field(self.ticketslot,200)
self.debug1:add_field(self.pin_admin,200)
self.debug1:add_field(self.adm_pin_new,200)
self.debug1:add_field(self.adm_tarif,200)
self.debug1:add_field(self.adm_tarif2,200)
self.debug1:add_field(self.adm_tarif_def,200)
self.debug1:add_field(self.adm_tarif_def2,200)
self.debug1:add_field(self.no_sdcard,200)
feed_the_dog()
self.model_nr = 1
if SERIALNUMBER == '0125377' then self.model_nr = 0
elseif SERIALNUMBER == '0132951' then self.model_nr = 0
elseif SERIALNUMBER == '0170833' then self.model_nr = 0
elseif SERIALNUMBER == '0171393' then self.model_nr = 0
elseif SERIALNUMBER == '0221177' then self.model_nr = 0
elseif SERIALNUMBER == '0221543' then self.model_nr = 0
elseif SERIALNUMBER == '0225791' then self.model_nr = 0
elseif SERIALNUMBER == '0244179' then self.model_nr = 0
elseif SERIALNUMBER == '0253954' then self.model_nr = 0
elseif SERIALNUMBER == '0332942' then self.model_nr = 0
elseif SERIALNUMBER == '0345748' then self.model_nr = 0
elseif SERIALNUMBER == '0353117' then self.model_nr = 0
elseif SERIALNUMBER == '0421683' then self.model_nr = 0
elseif SERIALNUMBER == '0432779' then self.model_nr = 0
elseif SERIALNUMBER == '0432781' then self.model_nr = 0
elseif SERIALNUMBER == '0437221' then self.model_nr = 0
elseif SERIALNUMBER == '0510487' then self.model_nr = 0
elseif SERIALNUMBER == '0518898' then self.model_nr = 0
elseif SERIALNUMBER == '0541273' then self.model_nr = 0
elseif SERIALNUMBER == '0553227' then self.model_nr = 0
elseif SERIALNUMBER == '0579312' then self.model_nr = 0
elseif SERIALNUMBER == '0614825' then self.model_nr = 0
elseif SERIALNUMBER == 'xxx0622171' then self.model_nr = 0 -- Frickenhausen
elseif SERIALNUMBER == '0635710' then self.model_nr = 0
elseif SERIALNUMBER == '0717833' then self.model_nr = 0
elseif SERIALNUMBER == '0722615' then self.model_nr = 0
elseif SERIALNUMBER == '0742293' then self.model_nr = 0
elseif SERIALNUMBER == '0746291' then self.model_nr = 0
elseif SERIALNUMBER == '0769931' then self.model_nr = 0
elseif SERIALNUMBER == '0789641' then self.model_nr = 0
elseif SERIALNUMBER == '0802931' then self.model_nr = 0
elseif SERIALNUMBER == '9999999' then self.model_nr = 0
end
self:init2()
end
--**********************************************************************
function App.init2 (self)
self.reset_device = 0 -- button 17
self.reset_device_ja = Field:new(self,'', self.adm_device,218)
self.reset_device_nein = Field:new(self,'', self.adm_device,217)
self.reset_ok_code = Field:new(self,'', self.adm_device,12)
self.ser_number = Field:new(self,'0',self.adm_device,21)
self.git_version = Field:new(self,'0',self.adm_device,22)
self.patchv = Field:new(self,'none',self.adm_device,23)
self.firmware = Field:new(self,'1.0',self.adm_device,24)
self.screen_nr = Field:new(self,'1.0',self.adm_device,25)
self.ueberdeckung = Field:new(self,'')
self.ueberdeckung:add_field(self.lang_layout3,94)
self.ueberdeckung:add_field(self.lang_layout2,94)
self.ueberdeckung:add_field(self.lang_layout1,94)
self.ueberdeckung:add_field(self.coin,94)
self.ueberdeckung:add_field(self.pin,94)
self.ueberdeckung:add_field(self.qrcode,94)
self.ueberdeckung:set_visiable(-1)
self.show_low = Field:new(self,'')
self.show_low:add_field(self.lang_layout3,98)
self.show_low:add_field(self.lang_layout2,98)
self.show_low:add_field(self.lang_layout1,98)
self.show_low:add_field(self.coin,98)
self.show_low:add_field(self.qrcode,98)
self.show_low:set_visiable(1)
self.text1 = Field:new(self,'')
self.text1:add_field(self.coin,270)
self.text2 = Field:new(self,'')
self.text2:add_field(self.coin,277)
self.text3 = Field:new(self,'')
self.text3:add_field(self.coin,278)
self.text4 = Field:new(self,'')
self.text4:add_field(self.coin,279)
self.muenzen = Field:new(self,'')
self.muenzen:add_field(self.lang_layout3,82)
self.muenzen:add_field(self.lang_layout2,82)
self.muenzen:add_field(self.lang_layout1,82)
self.muenzen:add_field(self.coin,82)
self.muenzen:add_field(self.lang_layout3,83)
self.muenzen:add_field(self.lang_layout2,83)
self.muenzen:add_field(self.lang_layout1,83)
self.muenzen:add_field(self.coin,83)
self.muenzen:add_field(self.lang_layout3,84)
self.muenzen:add_field(self.lang_layout2,84)
self.muenzen:add_field(self.lang_layout1,84)
self.muenzen:add_field(self.coin,84)
self.muenzen1 = Field:new(self,'20 Ct')
self.muenzen1:add_field(self.lang_layout3,282)
self.muenzen1:add_field(self.lang_layout2,282)
self.muenzen1:add_field(self.lang_layout1,282)
self.muenzen1:add_field(self.coin,282)
self.muenzen2 = Field:new(self,'50 Ct')
self.muenzen2:add_field(self.lang_layout3,283)
self.muenzen2:add_field(self.lang_layout2,283)
self.muenzen2:add_field(self.lang_layout1,283)
self.muenzen2:add_field(self.coin,283)
self.muenzen3 = Field:new(self,'1 Euro')
self.muenzen3:add_field(self.lang_layout3,284)
self.muenzen3:add_field(self.lang_layout2,284)
self.muenzen3:add_field(self.lang_layout1,284)
self.muenzen3:add_field(self.coin,284)
self.bargeldlos = Field:new(self,'')
self.bargeldlos:add_field(self.lang_layout3,171)
self.bargeldlos:add_field(self.lang_layout2,171)
self.bargeldlos:add_field(self.lang_layout1,171)
self.bargeldlos:add_field(self.coin,171)
self.shortinfo = Field:new(self,'')
self.shortinfo:add_field(self.lang_layout3,170)
self.shortinfo:add_field(self.lang_layout2,170)
self.shortinfo:add_field(self.lang_layout1,170)
self.shortinfo:add_field(self.coin,170)
self.knob1 = Field:new(self,'')
self.knob1:add_field(self.lang_layout3,95)
self.knob1:add_field(self.lang_layout2,95)
self.knob1:add_field(self.lang_layout1,95)
self.knob1:add_field(self.coin,95)
self.knob2 = Field:new(self,'')
self.knob2:add_field(self.lang_layout3,96)
self.knob2:add_field(self.lang_layout2,96)
self.knob2:add_field(self.lang_layout1,96)
self.knob2:add_field(self.coin,96)
self.sondertarif_anzeige = Field:new(self,'',self.coin,3)
self.nochmal_button = Field:new(self,'',self.coin,4)
self.nochmal = Field:new(self,'',self.coin,204)
self.tarifnr = 1
self.pin_feld = Field:new(self,'',self.pin,1)
self.price_per_hour = { Field:new(self,DEFAULT_price_per_hour_1), Field:new(self,DEFAULT_price_per_hour_2) }
self.price_per_hour[1]:add_field(self.adm_tarif,12)
self.price_per_hour[1]:add_flash_position(50,10)
self.price_per_hour[2]:add_field(self.adm_tarif2,12)
self.price_per_hour[2]:add_flash_position(850,10)
self.min_sum = { Field:new(self,DEFAULT_min_sum_1), Field:new(self,DEFAULT_min_sum_2) }
self.min_sum[1]:add_field(self.adm_tarif,9)
self.min_sum[1]:add_flash_position(70,10)
self.min_sum[2]:add_field(self.adm_tarif2,9)
self.min_sum[2]:add_flash_position(870,10)
self.max_park_hour = { Field:new(self,DEFAULT_max_park_hour_1), Field:new(self,DEFAULT_max_park_hour_2) }
self.max_park_hour[1]:add_field(self.adm_tarif,10)
self.max_park_hour[1]:add_flash_position(90,5)
self.max_park_hour[2]:add_field(self.adm_tarif2,10)
self.max_park_hour[2]:add_flash_position(890,5)
self.max_park_minute = { Field:new(self,DEFAULT_max_park_minute_1), Field:new(self,DEFAULT_max_park_minute_2) }
self.max_park_minute[1]:add_field(self.adm_tarif,14)
self.max_park_minute[1]:add_flash_position(100,5)
self.max_park_minute[2]:add_field(self.adm_tarif2,14)
self.max_park_minute[2]:add_flash_position(900,5)
self.tagesticket = { Field:new(self,DEFAULT_tagesticket_1), Field:new(self,DEFAULT_tagesticket_2) } -- button 17
self.tagesticket[1]:add_flash_position(110,5)
self.tagesticket[2]:add_flash_position(910,5)
self.tagesticket_ja = { Field:new(self,''), Field:new(self,'') }
self.tagesticket_ja[1]:add_field(self.adm_tarif,218)
self.tagesticket_ja[2]:add_field(self.adm_tarif2,218)
self.tagesticket_nein = { Field:new(self,'0'), Field:new(self,'0') }
self.tagesticket_nein[1]:add_field(self.adm_tarif,217)
self.tagesticket_nein[2]:add_field(self.adm_tarif2,217)
self.pay_mode = { Field:new(self,DEFAULT_pay_mode_1), Field:new(self,DEFAULT_pay_mode_2) } -- button 3
self.pay_mode[1]:add_flash_position(120,5) -- Werte: 0 / 1 / 2
self.pay_mode[2]:add_flash_position(920,5)
self.pay_mode_coins = { Field:new(self,'0'), Field:new(self,'0') }
self.pay_mode_coins[1]:add_field(self.adm_tarif,220)
self.pay_mode_coins[2]:add_field(self.adm_tarif2,220)
self.pay_mode_qrcode = { Field:new(self,'0'), Field:new(self,'0') }
self.pay_mode_qrcode[1]:add_field(self.adm_tarif,221)
self.pay_mode_qrcode[2]:add_field(self.adm_tarif2,221)
self.pay_mode_both = { Field:new(self,'0'), Field:new(self,'0') }
self.pay_mode_both[1]:add_field(self.adm_tarif,222)
self.pay_mode_both[2]:add_field(self.adm_tarif2,222)
self.tax_vat = Field:new(self,DEFAULT_tax_vat)
self.tax_vat:add_field(self.adm_tarif,2)
self.tax_vat:add_field(self.adm_tarif2,2)
self.tax_vat:add_flash_position(130,10)
self.max_zeilen = Field:new(self,DEFAULT_max_zeilen)
self.max_zeilen:add_field(self.adm_proto,5)
self.max_zeilen:add_flash_position(280,10)
self.reportnumber = Field:new(self,DEFAULT_reportnumber)
self.reportnumber:add_field(self.adm_proto,6)
self.reportnumber:add_flash_position(290,10)
self.report_wird_gedruckt = Field:new(self,'',self.adm_proto,205)
self.is_tarif2 = Field:new(self,'', self.adm_tarif,20)
self.location1 = Field:new(self,DEFAULT_location1)
self.location1:add_field(self.adm_basic,14)
self.location1:add_flash_position(140,10)
self.location2 = Field:new(self,DEFAULT_location2)
self.location2:add_field(self.adm_basic,10)
self.location2:add_flash_position(180,10)
self.location3 = Field:new(self,DEFAULT_location3)
self.location3:add_field(self.adm_basic,15)
self.location3:add_flash_position(220,10)
self.lang_description1 = Field:new(self,'')
self.lang_description1:add_field(self.lang_layout3,4)
self.lang_description1:add_field(self.lang_layout2,4)
self.lang_description1:add_field(self.lang_layout1,4)
self.lang_description2 = Field:new(self,'')
self.lang_description2:add_field(self.lang_layout3,5)
self.lang_description2:add_field(self.lang_layout2,5)
self.lang_description3 = Field:new(self,'')
self.lang_description3:add_field(self.lang_layout3,6)
feed_the_dog()
self.country_table = {}
local zaehler = 0
while zaehler < 20 do
zaehler = zaehler + 1
if FLAGS[zaehler] == nil or zaehler > #LANGUAGES then
table.insert(self.country_table," "..string.sub("1234567890abcdefghijk",zaehler,zaehler)) -- man muss
else -- die Sprachen unterscheiden, auch wenn das stillgelegte Dummy-Sprachen sind
if FLAGS[zaehler] == "UK" then
table.insert(self.country_table,"EN")
else
table.insert(self.country_table,FLAGS[zaehler])
end
end
end
self.ldescriptions = {}
self.lang1 = {}
self.lang2 = {}
self.lang3 = {}
local first_three_languages = ""
for i,lang in ipairs( self.country_table ) do
self.lang1[lang] = Field:new(self,lang)
self.lang1[lang]:add_field(self.lang_layout3, 9+i)
self.lang1[lang]:add_field(self.lang_layout2, 9+i)
self.lang1[lang]:add_field(self.lang_layout1, 9+i)
self.lang2[lang] = Field:new(self,lang)
self.lang2[lang]:add_field(self.lang_layout3, #self.country_table+9+i)
self.lang2[lang]:add_field(self.lang_layout2, #self.country_table+9+i)
self.lang3[lang] = Field:new(self,lang)
self.lang3[lang]:add_field(self.lang_layout3, 2*#self.country_table+9+i)
-- if LANGUAGE_FILTER == "ALL" or string.find(","..LANGUAGE_FILTER..",",","..lang..",") then
first_three_languages = first_three_languages .. lang
if string.sub(lang,1,1) == " " then -- dummy language, starting with blank
self.adm_lang_sel:set_text(20+#first_three_languages/2," ")
else
table.insert(self.ldescriptions,t("LANG",i))
self.adm_lang_sel:set_text(20+#first_three_languages/2,lang)
end
-- end
-- if lang == string.sub(self.lang_selector.default_text,1,2) and self.simulation == 0 then
-- set_language(i-1)
-- lang_nr = i+1
-- end
end
self.flags_set = ''
self.lang_selector = Field:new(self,string.sub(first_three_languages,1,6))
self.lang_selector:add_flash_position(260,10)
self.reportlang = Field:new(self,string.sub(first_three_languages,1,2))
self.reportlang:add_flash_position(273,4)
self.reportlang:add_field(self.adm_basic,216)
self.reportlang1 = Field:new(self,'Protokolle:')
self.reportlang1:add_field(self.adm_basic,215)
-- set_text(1,200,"hier1243 " .. "")
-- change_screen(1) end function xxx ()
-- self.ticketslot_pin = Field:new(self,'', self.ticketslot,2)
self.pin_start = Field:new(self,'',self.adm_pin_new,6)
self.ticketslot_pin = Field:new(self,'')
self.ticketslot_pin:add_flash_position(310,100)
self.ticketslot_tan1 = Field:new(self,'', self.ticketslot,1)
self.ticketslot_tan2 = Field:new(self,'', self.ticketslot,3)
self.ticketslot_tan3 = Field:new(self,'', self.ticketslot,4)
self.anzeige_betrag = Field:new(self,'0,00', self.coin,73)
self.anzeige_zusatztag = Field:new(self,'0,00', self.coin,54)
self.anzeige_drucken = Field:new(self,'',self.coin,65)
self.anzeige_parkdauer = Field:new(self,'',self.coin,72)
self.qrcode_text = Field:new(self,'',self.qrcode,1) -- in dieses Feld kann der QR-Code eingetragen werden
self.qrtan = Field:new(self,'',self.qrcode,2)
self.qr_invalid = Field:new(self,'',self.qrcode,3)
self.qrtaste = {}
for nr = 1,12,1 do
self.qrtaste[nr] = Field:new(self,'',self.qrcode,nr+9)
end
Field:new(self,'',self.qrcode,3)
if file_open == nil then
self.anzeige_parkdauer:add_field(self.standby,17)
self.parkzeit = Field:new(self,'', self.standby,13)
self.header_message = Field:new(self,'', self.standby,23)
self.pluse_sum = Field:new(self,'', self.standby,2)
self.press_button = Field:new(self,'', self.standby,21)
end
self.setup_lang1 = Field:new(self,'', self.adm_basic,20)
self.setup_lang2 = Field:new(self,'', self.adm_basic,21)
self.setup_lang3 = Field:new(self,'', self.adm_basic,22)
self.pin_definitions = Field:new(self,DEFAULT_pin_definitions)
self.pin_definitions:add_flash_position(15*4096+5,4000)
self.pin_offset = 0
self.ungueltig_sec = Field:new(self,'', self.pin,33)
self.ungueltige_pin = Field:new(self,'', self.pin,3)
self.ungueltig_text = Field:new(self,'Ungültige PIN', self.pin,202)
self.pin_wartezeit = 0
feed_the_dog()
self.pin_entries = {}
while #self.pin_entries < 10 do
table.insert(self.pin_entries, {
Field:new(self,'', self.pin_admin,#self.pin_entries+1),
Field:new(self,'', self.pin_admin,#self.pin_entries+21),
Field:new(self,'', self.pin_admin,#self.pin_entries+51)
} )
end
-- if string.sub(self.location1:read(),1,1) == "+" then
-- function print (text)
-- my_write_filedata1("debug3.txt",sd_dir..'/'.."debug3.txt",text.."\n",add_write)
---- beep(100)
-- end
-- end
self:init3()
end
--**********************************************************************
function App.init3 (self)
self.tarif_par = { {}, {}, {}, {} }
self.tarif_par[1][1] = Field:new(self,DEFAULT_tarif_1_1,self.adm_tarif_def ,20+1)
self.tarif_par[2][1] = Field:new(self,DEFAULT_tarif_2_1,self.adm_tarif_def ,40+1)
self.tarif_par[3][1] = Field:new(self,DEFAULT_tarif_3_1,self.adm_tarif_def2,20+1)
self.tarif_par[4][1] = Field:new(self,DEFAULT_tarif_4_1,self.adm_tarif_def2,40+1)
self.tarif_par[1][2] = Field:new(self,DEFAULT_tarif_1_2,self.adm_tarif_def ,20+2)
self.tarif_par[2][2] = Field:new(self,DEFAULT_tarif_2_2,self.adm_tarif_def ,40+2)
self.tarif_par[3][2] = Field:new(self,DEFAULT_tarif_3_2,self.adm_tarif_def2,20+2)
self.tarif_par[4][2] = Field:new(self,DEFAULT_tarif_4_2,self.adm_tarif_def2,40+2)
self.tarif_par[1][3] = Field:new(self,DEFAULT_tarif_1_3,self.adm_tarif_def ,20+3)
self.tarif_par[2][3] = Field:new(self,DEFAULT_tarif_2_3,self.adm_tarif_def ,40+3)
self.tarif_par[3][3] = Field:new(self,DEFAULT_tarif_3_3,self.adm_tarif_def2,20+3)
self.tarif_par[4][3] = Field:new(self,DEFAULT_tarif_4_3,self.adm_tarif_def2,40+3)
self.tarif_par[1][4] = Field:new(self,DEFAULT_tarif_1_4,self.adm_tarif_def ,20+4)
self.tarif_par[2][4] = Field:new(self,DEFAULT_tarif_2_4,self.adm_tarif_def ,40+4)
self.tarif_par[3][4] = Field:new(self,DEFAULT_tarif_3_4,self.adm_tarif_def2,20+4)
self.tarif_par[4][4] = Field:new(self,DEFAULT_tarif_4_4,self.adm_tarif_def2,40+4)
self.tarif_par[1][5] = Field:new(self,DEFAULT_tarif_1_5,self.adm_tarif_def ,20+5)
self.tarif_par[2][5] = Field:new(self,DEFAULT_tarif_2_5,self.adm_tarif_def ,40+5)
self.tarif_par[3][5] = Field:new(self,DEFAULT_tarif_3_5,self.adm_tarif_def2,20+5)
self.tarif_par[4][5] = Field:new(self,DEFAULT_tarif_4_5,self.adm_tarif_def2,40+5)
self.tarif_par[1][6] = Field:new(self,DEFAULT_tarif_1_6,self.adm_tarif_def ,20+6)
self.tarif_par[2][6] = Field:new(self,DEFAULT_tarif_2_6,self.adm_tarif_def ,40+6)
self.tarif_par[3][6] = Field:new(self,DEFAULT_tarif_3_6,self.adm_tarif_def2,20+6)
self.tarif_par[4][6] = Field:new(self,DEFAULT_tarif_4_6,self.adm_tarif_def2,40+6)
self.tarif_par[1][7] = Field:new(self,DEFAULT_tarif_1_7,self.adm_tarif_def ,20+7)
self.tarif_par[2][7] = Field:new(self,DEFAULT_tarif_2_7,self.adm_tarif_def ,40+7)
self.tarif_par[3][7] = Field:new(self,DEFAULT_tarif_3_7,self.adm_tarif_def2,20+7)
self.tarif_par[4][7] = Field:new(self,DEFAULT_tarif_4_7,self.adm_tarif_def2,40+7)
self.tarif_par[1][8] = Field:new(self,DEFAULT_tarif_1_8,self.adm_tarif_def ,20+8)
self.tarif_par[2][8] = Field:new(self,DEFAULT_tarif_2_8,self.adm_tarif_def ,40+8)
self.tarif_par[3][8] = Field:new(self,DEFAULT_tarif_3_8,self.adm_tarif_def2,20+8)
self.tarif_par[4][8] = Field:new(self,DEFAULT_tarif_4_8,self.adm_tarif_def2,40+8)
self.tarif_par[1][9] = Field:new(self,DEFAULT_tarif_1_9,self.adm_tarif_def ,20+9)
self.tarif_par[2][9] = Field:new(self,DEFAULT_tarif_2_9,self.adm_tarif_def ,40+9)
self.tarif_par[3][9] = Field:new(self,DEFAULT_tarif_3_9,self.adm_tarif_def2,20+9)
self.tarif_par[4][9] = Field:new(self,DEFAULT_tarif_4_9,self.adm_tarif_def2,40+9)
self.tarif_par[1][10] = Field:new(self,DEFAULT_tarif_1_10,self.adm_tarif_def ,20+10)
self.tarif_par[2][10] = Field:new(self,DEFAULT_tarif_2_10,self.adm_tarif_def ,40+10)
self.tarif_par[3][10] = Field:new(self,DEFAULT_tarif_3_10,self.adm_tarif_def2,20+10)
self.tarif_par[4][10] = Field:new(self,DEFAULT_tarif_4_10,self.adm_tarif_def2,40+10)
for i = 1,10,1 do
-- self.tarif_par[1][i] = Field:new(self,'',self.adm_tarif_def ,20+i)
-- self.tarif_par[2][i] = Field:new(self,'',self.adm_tarif_def ,40+i)
-- self.tarif_par[3][i] = Field:new(self,'',self.adm_tarif_def2,20+i)
-- self.tarif_par[4][i] = Field:new(self,'',self.adm_tarif_def2,40+i)
self.tarif_par[1][i]:add_flash_position(1200+10*i,10)
self.tarif_par[2][i]:add_flash_position(1400+10*i,10)
self.tarif_par[3][i]:add_flash_position(1600+10*i,10)
self.tarif_par[4][i]:add_flash_position(1800+10*i,10)
end
local actual_language = 999
for i,country in ipairs(self.country_table) do
if self.country_table[i] == actual_country then
actual_language = self.ldescriptions[i]
end
end
feed_the_dog()
self:init4()
end
--**********************************************************************
function App.init4 (self)
-- Einrichten von 10 moeglichen Positionen fuer die Flash-Signatur,
-- wegen der Haeufigkeit des Schreibens einer Signatur
TESTPOINTS = '99999'
self.signature_positions = {}
for i = 0,9 do
table.insert(self.signature_positions,12300+i*4096)
end
-- Einrichten von zehn Countern, die in Summe den Zaehlerstand ergeben,
-- und heruntergezaehlt werden. Auch hier der Grund die Haeufigkeit
-- des Schreibens
-- das Geraet geht auf 'Geraet nicht betriebsbereit', wenn keine Punkte mehr da sind, und versucht wird, ein
-- Parkticket zu drucken. Man kann dann wieder anschalten und gleich ins Admin-Menue gehen um sich eine TAN
-- geben zu lassen. Das geht dann noch, und man kann das Geraet mit Punkten wieder aufladen.
self.counter = {}
for i = 0,9,1 do
local count = Field:new(self,'100') -- ein Punkt defaultmaessig aufgeladen
if file_open == nil then
count = Field:new(self,TESTPOINTS) -- '10000') -- im Testmodus 10 * TESTPOINTS Punkte verfuegbar
end
count:add_flash_position(12600+i*4096,100)
table.insert(self.counter,count)
end
self.aktueller_teilcounter = 0
self.counter1 = Field:new(self,'0', self.admin,8)
self.anzahl_gedruckte_parktickets = Field:new(self,'0', self.admin,7)
self.anzahl_gedruckte_parktickets:write('0')
self:make_tarif_table() -- geht leider nicht in init3(), da in diesem Befehl evtl. schon in den Flash geschrieben wird
-- for f,g in pairs(self.tarif_table[1]) do print(f,g[1],g[2]) end
feed_the_dog()
self.ggt = 0
for i,coinnr in ipairs(COINVALUE) do
self.ggt = math.floor( self:euklid(self.ggt,coinnr*100) + 0.00001 )
end
self.symbols_for_underlining = {"","","","","","","",""}
self.symbols_for_underlining [ 1+ 0*1 + 0*2 + 0*4 ] = "="
self.symbols_for_underlining [ 1+ 1*1 + 0*2 + 0*4 ] = ";"
self.symbols_for_underlining [ 1+ 0*1 + 1*2 + 0*4 ] = ","
self.symbols_for_underlining [ 1+ 1*1 + 1*2 + 0*4 ] = "o"
self.symbols_for_underlining [ 1+ 0*1 + 0*2 + 1*4 ] = "."
self.symbols_for_underlining [ 1+ 1*1 + 0*2 + 1*4 ] = ":"
self.symbols_for_underlining [ 1+ 0*1 + 1*2 + 1*4 ] = "."
self.symbols_for_underlining [ 1+ 1*1 + 1*2 + 1*4 ] = "-"
self:init5()
end
--**********************************************************************
function App.zeitumstellung (self,pin_defs)
-- stop_timer(27)
self.zeitumstellungs_zaehler = 0
local naechste_zeitumstellung = 9999999999 -- in Sekunden seit 1970
self.zeitumstellung_wert = 0 -- die Anzahl der Stunden, die bei der Zeitumstellung addiert bzw. abgezogen wird
if pin_defs == nil then
pin_defs = self:read_pin_defs()
end
local x1,x2 = string.find(pin_defs,'S%d%d%d%d%dT,')
local summertime_mode = 0
local summertime_state = 0 -- 1: Normalzeit, 2: Sommerzeit
self.shift_time = 1 -- Differenz in Stunden zu GMT bei Winterzeit
if x1 ~= nil then -- der Mode, aktueller Status, bzw. die die Differenz zu GMT bei Winterzeit - in Zehntel-Stunden - koennen zusasetzlich abgespeichert werden
summertime_mode = tonumber(string.sub(pin_defs,x1+1,x1+1))
summertime_state = tonumber(string.sub(pin_defs,x1+2,x1+2))
self.shift_time = tonumber(string.sub(pin_defs,x1+3,x1+5))
end
if summertime_mode == 0 then -- Summertime-Mode: Der grundlegende Algorithmus. Zur Zeit ist nur Mode 1 implementiert.
summertime_mode = 1 -- Mode 2 bedeutet: keinerlei Umstellung
end
if summertime_mode == 2 then
return
end
local akt_time = self:date("%y%m%d%H%M%S",self:time())
-- akt_time = "20251021005910" -- zu Testzwecken
if summertime_mode == 1 then -- hier der allgemeine Modus fuer die europaeischen Laender
local zaehler = 1
local endtage = "06958169581706817069580695"
local monate = {"10","03"}
local x1
while zaehler <= #endtage do
x1 = string.sub(endtage,zaehler,zaehler) -- Endtage bei Umstellung
if tonumber(x1) < 2 then
x1 = "3" .. x1
else
x1 = "2" .. x1
end -- in x1 steht nun genau die Tagesnummer der Zeitumstellung, nun noch den Monat bestimmen:
x1 = string.format("%04u",2025+math.floor(-0.2+zaehler/2)) .. monate[1+zaehler%2] .. x1 .. "030005"
-- print("XXX1111",x1)
if tonumber(akt_time) < tonumber(x1) then -- x1 ist also das naechste Wechseldatum in der Zukunft
break
end
zaehler = zaehler + 1
end
-- print("wwwwww",akt_time)
self.zeitumstellung_wert = 2*(zaehler%2) - 1 -- muss positiv sein bei Zeitumstellung nach Sommerzeit, sonst negativ
if summertime_state == 0 then -- State der zeit konnte nicht festegestellt werden. Nehmen wir also an, dass es sich um die aktuelle Winter-/Sommerzeit handelt
summertime_state = 2 - zaehler%2
end
-- print("====================================",summertime_state,zaehler,self.zeitumstellung_wert)
if summertime_state == 2 - zaehler%2 then -- Die Zeit ist richtig eingestellt
self.zeitumstellungs_zaehler = 0
self.zeitumstellungs_intervall = math.max(100000,1000*(self:time(x1) - self:time(akt_time))) -- Zeitumstellung nach fruehestens 100 Sekunden
print("milli",self.zeitumstellungs_intervall)
if self.zeitumstellungs_intervall < 100000000 then -- wenn die Zeitumstellung kleiner ist als 100000 Sekunden (also etwa ein Tag), dann gelich den Zaehler direkt aktivieren
self.zeitumstellungs_zaehler = 299 -- Zeitumstellung wird nach diesem Interval gleich aktiviert
else -- wenn groesser als ein Tag bis zur naechsten Zeitumstellung, dann das Intervall in 1000 abschnitte aufteilen,
self.zeitumstellungs_intervall = math.ceil(self.zeitumstellungs_intervall / 200) -- und mit einem Timer hochzaehlen
end
print(27,self.zeitumstellungs_intervall/8640,self.zeitumstellungs_zaehler)
start_timer(27,self.zeitumstellungs_intervall,0,1)
else -- die Zeit muss JETZT umgestellt werden
self:zeit_umstellen()
end
end
end
--**********************************************************************
function App.zeit_umstellen (self)
if self.ticket_wird_gerade_gedruckt == 1 then
start_timer(15,100000,0,1) -- spaeter nochmal versuchen
end
if self.anzeige_betrag:read() ~= "0,00" then
start_timer(15,200000,0,1) -- spaeter nochmal versuchen
end
local akt_time = self:time()
akt_time = akt_time + self.zeitumstellung_wert * 3600
akt_time = self:date("%y%m%d%H%M%S",akt_time)
self.debug1:write("AAA",akt_time)
set_date_time(string.sub(akt_time,1,4),string.sub(akt_time,5,6),string.sub(akt_time,7,8),
string.sub(akt_time,9,10),string.sub(akt_time,11,12),string.sub(akt_time,13,14))
local pin_defs = self:read_pin_defs()
local x1,x2 = string.find(pin_defs,'S%d%d%d%d%dT,')
if x1 ~= nil then -- falls es einen Speicherbereich gibt, den Status dadrin updaten
local summertime_state = 1
if self.zeitumstellung_wert > 0 then
summertime_state = 2
end
pin_defs = string.sub(pin_defs,1,x1+2) .. string.format("%1u",summertime_state) .. string.sub(pin_defs,x1+4)
self.pin_definitions:write(pin_defs)
end
self:zeitumstellung(pin_defs)
end
--**********************************************************************
function App.xxzeitumstellung (self,pin_defs)
-- set_date_time(string.sub(akt_time,1,4),string.sub(akt_time,5,6),string.sub(akt_time,7,8),
-- string.sub(akt_time,9,10),string.sub(akt_time,11,12),string.sub(akt_time,13,14))
set_date_time("2024","10","12","11","01","22")
end
--**********************************************************************
function App.euklid (self,a,b)
while b > 0 do
local h = a % b
a = b
b = h
end
return(a)
end
--**********************************************************************
function App.init5 (self)
self.last_print_date = ""
self.reportnumber_tmp = nil
if start_timer ~= nil then
start_timer(29,60000,0,1) -- nach einer Minute den ersten heartbeat-Eintrag machen
end
-- local a = xxx + 5 als Test
-- print("TRACEBACK",debug.traceback())
self.TIMEOUT = 18
self.REDUCE_BACKLIGHT = 14 -- Sekunden bis low Backlight
self.TIMEOUTLONG = 75
self.TIMEOUTQRCODE = 40.0
self.QRCODE_REDUCE = 32.0
self.LOW_BACKLIGHT = 30
self.BLACKSCREEN = 0
self.WAIT_TIME_LONG_PRESS = 3
self.ANZAHL_PINS = 5
self.NO_CHECK_FOR_SIGNATURE = 1
self.sondertarif_text = ''
self.reporthashes = "," -- hier werden die schon uebermittelten statischen Bestandteile von Reports als Hashes gemerkt
self.static_strings = {}
SE_CLASS = tonumber(SERIALNUMBER) % 3800 -- Serial nummer Klasse
local SE1 = math.floor(1 + SE_CLASS/62)
local SE2 = 1 + SE_CLASS%62
SE_CLASS = string.sub(CHAR64,SE1,SE1) .. string.sub(CHAR64,SE2,SE2)
if CURRENTSHORT == nil then
CURRENTSHORT = string.sub(CURRENT,1,1)
end
self.normal_test = ""
self.normal_test1 = ""
self:update_pruefcode()
local pd = self:read_pin_defs()
local x1 = nil
local x2 = nil
x1,x2 = string.find(pd,PRINT_BF_EXIST..",")
if x1 == nil then
PRINT_BF_EXIST = 0
else
PRINT_BF_EXIST = 1
end
-- self.shift_time = 0
-- self.zeitumstellung_wert = 0
self:zeitumstellung()
-- print(PRUEFCODE)
-- WAIT_26 = 1000
if file_open == nil then
self.LOW_BACKLIGHT = 70
self.BLACKSCREEN = 68
end
self.pin_randomseed = Field:new(self, DEFAULT_pin_randomseed )
self.pin_randomseed:add_flash_position(14*4096+72,10)
-- if self.simulation == 1 then
-- os.execute('mkdir flashsim 2> /dev/null')
-- end
-- self:write_flash(22,'a')
-- local h = self:read_flash(22)
if self.simulation == 1 then
return
end
self.who_logged_in_as_admin = ''
self.last_entered_pin = ''
-- check the integrity of flash
self.screen_switch_is_blocked = 0
self.no_timeout = 0
self.admin_count = 0
self.control0 = nil
self.aktuelle_pin_eingabe = ''
self.long_press = 0
self.catch_release = 0
self.lfd_nr = tonumber(self:time()) % 100
self.last_print_datum = ''
self.sondertarif_text = ''
self.ser_number:write(SERIALNUMBER)
local g1, g2 = string.find(GIT_COMMIT,"%(")
if GIT_COMMIT0 == nil then
self.git_version:write(GIT_COMMIT)
self.patchv:write( 'none' )
self.git_id = bin.hextos(string.sub(GIT_COMMIT,g1+1,g1+8) .. string.sub(GIT_COMMIT,g1+1,g1+8))
else
local h1, h2 = string.find(GIT_COMMIT0,"%(")
self.git_version:write(GIT_COMMIT0)
self.patchv:write( GIT_COMMIT )
self.git_id = bin.hextos(string.sub(GIT_COMMIT0,h1+1,h1+8) .. string.sub(GIT_COMMIT,g1+1,g1+8))
end
self.firmware:write(FIRMWARE_VER)
self.screen_nr:write( get_version() )
-- self.used_pins = {}
self.used_pin_groups = {}
self.datum_aktuelle_ticketliste = ""
self:set_lang_nr_for_report()
self.adm_basic:set_text(20,string.sub(self.lang_selector:read(),1,2))
self.adm_basic:set_text(21,string.sub(self.lang_selector:read(),3,4))
self.adm_basic:set_text(22,string.sub(self.lang_selector:read(),5,6))
self:synchronize_buttons()
self:check_if_tarif2_is_necessary()
if SERIALNUMBER == "9999999" and file_open ~= nil then
self.debug1:set_visiable(1) -- @@ test, Aktivieren für den gruenen Debug-Balken
self.LOW_BACKLIGHT = 70
self.BLACKSCREEN = 50
else
self.debug1:set_visiable(0)
end
self.nochmal:set_visiable(-1)
self.nochmal_button:set_visiable(-1)
self.knob1:set_visiable(1)
self.knob2:set_visiable(-1)
print("HAS_KNOB",HAS_KNOB)
if HAS_KNOB == 1 then
self.knob1:set_visiable(-1) -- Schaltflaeche deaktiviert, denn es gibt ja einen Knopf
end
-- ZUFALLSWERT = 1
self:reset_coin()
self.qr_invalid:set_visiable(0)
-- self.debug1:write(ZUFALLSWERT)
if RESET_FLASH > 0 then -- mit dieswer Einstellung kann also der ganze interne Flash geloescht werden
self:check_for_flash_reset('341674')
self:disable_device(1)
return
end
-- self.uartbuffer = {}
-- self.uartzaehler = 0
-- start_timer(7,100,0,1)
self:switch_language(1)
if file_open ~= nil and sd_dir == '---' then
set_backlight(100)
lang_nr = self.lang_nr_for_report
-- self.no_sdcard:activate("force") -- keine SD Karte eingelegt (Bitte SD-Karte einlegen)
-- return
end
self.there_is_no_sdcard = 0
feed_the_dog()
self:compute_shortinfo()
if self.simulation == 0 then
self:no_simulation()
end
self.report_is_integer = 1
self.normal_test1 = "1431"
local rep = self:read_report(1) -- mit Check, ob vielleicht das letzte Parkticket verlorengegangen war, oder der Counter eines uebersehen hat, und Korrektur
if rep == nil then -- wenn eine Korrektur vorgenommen wurde, Battery Alert vorbereiten, dann hier aussteigen
my_write_filedata1("battery.txt",sd_dir..'/'.."battery.txt","xxxxxxxxxxxxxxxx\n",over_write)
self:disable_device()
return
end
self.normal_test = "1333"
self:write_report(rep)
rep = self:check_for_battery_alert()
if rep == 1 then -- wenn der Battery Alert ausgedruckt wurde: Battery Alert ausschalten, dann hier auch aussteigen, erst dann beim Neustart weiter
my_write_filedata1("battery.txt",sd_dir..'/'.."battery.txt"," \n",over_write) -- nochmal zur Sicherheit ueberschreiben
self:disable_device()
return
end
self.request_id = 0
self.flash_is_not_signed = 1 - self:check_flash_signature()
self.ticketslot_start = 0
if true and (self.flash_is_not_signed == 1 or self.report_is_integer == 0) then
self:print('flash was not integer....')
self.no_timeout = 1
self:renew_timeout()
self.ticketslot_start = 1
lang_nr = self.lang_nr_for_report
self.ticketslot:activate("force")
-- self.adm_proto:activate() -- zu Testzwecken
else
self:update_counter(0)
self.no_timeout = 0
self.machine_is_running = 1
-- set_backlight(self.BLACKSCREEN)
self.standby:activate()
end
self.debug1:write("DISABLED" .. tostring(DISABLED) )
-- self:check_for_nightmode()
-- print("NIGHT",self.nightmode)
self:switch_language(1)
start_timer(17,2000,0,1) -- reset all, to be safe, reset_coin
start_timer(16,2500,0,1) -- unlock uart_sperre, manchmal kommen sonst beim Start schon Betraege
-- print("SCR---------------------------",self.standby.screen_id,self.standby,self.screens[1])
-- self.counter1:write( string.format('%7u',math.floor(self:read_counter()/100+0.5)) )
-- self.anzahl_gedruckte_parktickets:write('0')
start_timer(14,15000,0,1) -- Ausschalten feed the dog watchdog
end
--****************************************************
function App.on_screen_change (self,sid)
if self.screens ~= nil then
self.aktueller_screen = self.screens[sid]
end
end
--****************************************************
function App.synchronize_buttons (self,bval)
bval = tonumber(self.tagesticket[1]:read())
self.tagesticket[1]:set_visiable(0)
self.tagesticket_ja[1]:set_visiable( (bval==1) and 1 or 0 )
self.tagesticket_nein[1]:set_visiable((bval==0) and 1 or 0 )
bval = tonumber(self.tagesticket[2]:read())
self.tagesticket[2]:set_visiable(0)
self.tagesticket_ja[2]:set_visiable( (bval==1) and 1 or 0 )
self.tagesticket_nein[2]:set_visiable((bval==0) and 1 or 0 )
bval = tonumber(self.pay_mode[1]:read())
self.pay_mode[1]:set_visiable(0)
self.pay_mode_coins[1]:set_visiable( (bval==0) and 1 or 0 )
self.pay_mode_qrcode[1]:set_visiable( (bval==1) and 1 or 0 )
self.pay_mode_both[1]:set_visiable( (bval==2) and 1 or 0 )
bval = tonumber(self.pay_mode[2]:read())
self.pay_mode[2]:set_visiable(0)
self.pay_mode_coins[2]:set_visiable( (bval==0) and 1 or 0 )
self.pay_mode_qrcode[2]:set_visiable( (bval==1) and 1 or 0 )
self.pay_mode_both[2]:set_visiable( (bval==2) and 1 or 0 )
bval = self.reset_device
self.reset_device_ja:set_visiable( (bval==1) and 1 or 0 )
self.reset_device_nein:set_visiable((bval==0) and 1 or 0 )
end
--**********************************************************************
function App.log (self,text)
local text1 = self:date("%y%m%d%H%M%S") .. " " .. text .. "\n"
my_write_filedata1("l35x.txt",sd_dir.."/".."l35x.txt",text1,add_write)
end
--**********************************************************************
function App.date (self,format_string,sec) -- gives formatted date/time strings from yyyymmddhhmmss or sec
if sec == nil then
if file_open == nil then
sec = os.time()
else
sec = App.time() -- without parameter sec: actual time
end
end
if format_string == nil then
return sec
end
if sec > 1000000000000 then -- the point in time is given as yyyymmddHHMMSS
sec = tostring(sec)
format_string = string.gsub(format_string,'%%y',string.sub(sec,1,4))
format_string = string.gsub(format_string,'%%m',string.sub(sec,5,6))
format_string = string.gsub(format_string,'%%d',string.sub(sec,7,8))
format_string = string.gsub(format_string,'%%H',string.sub(sec,9,10))
format_string = string.gsub(format_string,'%%M',string.sub(sec,11,12))
format_string = string.gsub(format_string,'%%S',string.sub(sec,13,14))
return format_string -- and is returned due to the format string
else
return date1.date_format(format_string,sec) -- returns the formatted time
end
end
--**********************************************************************
function App.time (self,tt) -- gives the secs of a yyyymmddHHMMSS string
local year = ''
local month = ''
local day = ''
local hour = ''
local min = ''
local sec = ''
local week = ''
if tt ~= nil then
year = string.sub(tt,1,4)
month = string.sub(tt,5,6)
day = string.sub(tt,7,8)
hour = string.sub(tt,9,10)
min = string.sub(tt,11,12)
sec = string.sub(tt,13,14)
else
if file_open == nil then
return os.time()
else
year, month, day, hour, min, sec, week = get_date_time()
print(year,month,day,hour,min,sec,week)
-- hour = hour - 12 @@ zu Testzwecken
end
end
local sec = date1.date_reverse(tonumber(year),tonumber(month),tonumber(day),tonumber(hour),tonumber(min),tonumber(sec))
return sec
end
--**********************************************************************
function App.check_for_flash_reset (self,xcode)
print('FLASH RESET')
-- if restart_device == 0 then
-- return
-- end
local patterns = { 341674, 627856, 524516, 788420, 634154 }
if xcode ~= nil and #xcode == 0 then -- soft reset
print(' ... soft')
for name,field in pairs(self.fields) do
feed_the_dog()
local bed = 0
for i = 1,10,1 do
if field == self.counter[i] then
bed = 1
break
end
end
if bed == 0 then
field:write( field.default_text )
end
end
self:check_flash_signature('update')
self.adm_soft_reset:activate()
self:disable_device("x")
flush_flash()
return
end
if xcode == nil and file_open ~= nil then
xcode = my_read_filedata(sd_dir..'/flash_reset.txt')
xcode = string.sub(xcode,1,6)
end
if xcode ~= nil then -- hard reset
for i,pp in ipairs(patterns) do
if xcode == tostring(pp) then
self:disable_device("x")
print(' ... hard')
self.comment = "HARD"
-- for zaehler = 0,31,1 do
-- print(zaehler)
-- feed_the_dog()
-- write_flash_string(1+zaehler*2048, string.rep(' ',2040))
-- end
for zaehler = 0,511,1 do
print(zaehler)
feed_the_dog()
write_flash_string(1+zaehler*128, string.rep('j',123))
if zaehler % 20 == 0 then
self.comment = self.comment .. tostring(zaehler) .. " \n"
-- self.comment = self.comment .. read_flash_string(1+zaehler*128) .. "\n"
end
end
flush_flash()
change_screen(13)
return
end
end
end
end
--**********************************************************************
function App.xxcheck_for_nightmode (self)
self.nightmode = ""
local pin_defs = self:read_pin_defs()
local x1,x2 = string.find(pin_defs,'NIGHT%d%d%d%d%d%d%d%d%d%d%sd%d')
-- x1 = 1
if x1 ~= nil then
self.nightmode = string.sub(pin_defs,x1+5,x1+5+11)
self.nightmode = "180007000100"
self.abendsec = tonumber(string.sub(self.nightmode,1,2))*3600 + tonumber(string.sub(self.nightmode,3,4))*60
self.morgensec = tonumber(string.sub(self.nightmode,5,6))*3600 + tonumber(string.sub(self.nightmode,7,8))*60
end
end
--**********************************************************************
function App.print (self,text1,text2,text3)
if self.debug ~= nil and file_open == nil then
if text2 == nil then
print(text1)
elseif text3 == nil then
print(text1,text2)
else
print(text1,text2,text3)
end
end
end
--**********************************************************************
function App.check_flash_signature (self,mode)
local contents = {}
for i,field in ipairs(self.fields) do
local nr = field.flash_position
local tt = field:read()
-- print(nr,tt)
if nr < 272 or nr > 275 or not mode == 'old_version' then -- old version: without reportlang-field
-- print("NN",nr)
table.insert(contents,{ nr, tt })
end
end
-- table.sort(contents,function (a,b) return(a[1]<b[1]) end)
local text = 'x'
-- local rtxt1 = "--------------------------\r\n"
for i,text1 in ipairs(contents) do
-- rtxt1 = rtxt1 .. tostring(text1[1]) .. ","..text1[2].."\r\n"
-- print("SORT",text1[1],text1[2])
text = text .. text1[2]
end
-- print(text)
-- if file_open ~= nil then
-- my_write_filedata(sd_dir.."/"..CHECK_SIGN,text,over_write)
-- end
local md5key = bin.stohex( md5lib.hash(text) )
error2("flashsign01")
local ciph = self:crypt(sec_key3,md5key)
error2("flashsign02")
-- rtxt1 = rtxt1 ..ciph.."\r\n"
-- local printtext = my_read_filedata(sd_dir..'/check_sign.txt')
-- if printtext == nil then
-- printtext = ""
-- end
-- rtxt1 = printtext .. rtxt1
-- my_write_filedata(sd_dir..'/check_sign.txt',rtxt1,over_write)
if mode == nil or mode == 'old_version' then
-- print("CIPH",ciph)
for i,sign_pos in ipairs(self.signature_positions) do -- alle moeglichen zehn Signatur-Positionen durchpruefen
if read_flash_string(sign_pos) == ciph then
return 1
end
end
if mode == 'old_version' then
return 0 --- nichts zu machen, weder mit neuer noch mit alter Version
end
return self:check_flash_signature('old_version') -- auch noch gegen die alte Version des Flash testen
end
print('SIGN FLASH',md5key)
local sign_idx = string.gsub(md5key,"%D","").."1"
-- zufaellig den naechsten Teilcounter setzen:
self.aktueller_teilcounter = tonumber( string.sub(sign_idx,1,10) % #self.counter)
-- zufaellig die Signatur-Position setzen:
self.sign_idx = string.sub(sign_idx,1,12)
sign_idx = tonumber( self.sign_idx ) % #self.signature_positions
-- print("XXWER",sign_idx)
local sign_pos = self.signature_positions[sign_idx+1]
-- print("SIX",sign_idx,sign_pos,ciph)
write_flash_string(sign_pos,ciph)
end
--**********************************************************************
function App.compute_tan (self,slcode)
sl = self:permutation(0,slcode)
if SERIALNUMBER ~= string.sub(sl,6,12) then
print( 'Serialnumber --> ', string.sub(sl,6,12) )
return
end
local pin = string.sub(sl,1,5)
local tan = self:check_tan(0,pin) -- zwoelfstellige TAN
-- print( 'PIN', pin, 'TAN: ', tan)
-- local ticketcode = self:permutation(1,pin..tan)
-- print( 'PIN: ', pin, tan,'TICKETCODE',ticketcode)
end
--**********************************************************************
function App.check_tan (self,mode,pin,tan)
error2("checktan01")
local ciph = self:crypt(sec_key1,pin)
error2("checktan02")
local md5c = bin.stohex( md5lib.hash(ciph) )
local md5_bits = bin.hextobin(md5c)
local tan1 = 0
for i,bit in ipairs(md5_bits) do
tan1 = 2*tan1 + tonumber(bit)
if i == 39 or (mode == 1 and i == 16) then
break
end
end
if mode == 1 then
tan1 = math.ceil(10^4 + 1.29 * tan1 + 1234) -- fuenfstellige TAN
else
tan1 = math.ceil(10^11 + 1.44 * tan1 + 1234) -- zwoelfstellige TAN
end
print('PIN/TAN/TAN1',pin,tan,tan1)
-- print('WWE',pin,tan,tan1)
if tan == nil then
return tostring(tan1)
end
if file_open == nil or SERIALNUMBER == "9999999" then
-- print("123456",tan)
if tan == '123412341234' or tan == '55555' then -- @@
return 1
end
end
if tostring(tan) == tostring(tan1) then
return 1
else
return 0
end
end
--**********************************************************************
function App.update_pruefcode (self)
self.is_parkticket = 1
self.has_voucher = 0
PRUEFCODE = 0
local max_zeilen = fcheck(0,tonumber,self.max_zeilen:read())
if max_zeilen < 0 then
PRUEFCODE = 10
max_zeilen = - max_zeilen
end
PRUEFCODE = PRUEFCODE + math.floor(max_zeilen/300+0.01)
max_zeilen = max_zeilen % 300
PRUEF_VALIDITY_SEC = self:time(PRUEF_VALIDITY .."000000")
QRCODE_VALIDITY_SEC = self:time(QRCODE_VALIDITY.."000000")
-- print("UPDATE:",PRUEFCODE)
return(max_zeilen)
end
--**********************************************************************
function App.permutation (self,nr,text)
local pm = {3,2,9,5,4,7,1,11,8,10,12,6}
zaehler = 0
text1 = {}
while zaehler < 12 do
zaehler = zaehler + 1
local i = zaehler + (2*nr-1)
if i == 0 then i = 12 end
if i == 13 then i = 1 end
i = pm[i]
text1[ pm[zaehler] ] = string.sub(text, i, i)
end
local text2 = ''
for i,x in ipairs(text1) do
text2 = text2 .. x
end
return text -- keine Permutation
end
--**********************************************************************
function App.request_flash_signature (self)
local tn = self.lang_nr_for_report
math.randomseed(tonumber(self:time())*1000+tonumber(get_timer_value(29)))
if self.flash_is_not_signed == 1 then
self.request_id = math.random(93003,99981)
elseif self.report_is_integer == 0 then
self.request_id = math.random(86003,92981)
end
self.debug1:write(tostring(self.request_id) .. ' ' .. tostring(self.report_is_integer)..' '..tostring(self:time()) )
local sl = tostring(self.request_id) .. trim(tostring(SERIALNUMBER))
sl = self:permutation(1,sl)
sl = 'S ' .. string.sub(sl,1,4) .. ' ' .. string.sub(sl,5,8) .. ' ' .. string.sub(sl,9,12)
-- print(sl)
local requestinfo =
' \n' ..
' \n' ..
'QQx1d@\n' ..
'QW\n' ..
'QW\n' ..
'QQx1ba0QQx1d!QQx00\n' ..
self:ext_line( t('Request for storage signing',tn)) .. '\n' ..
self:ext_line( t('by specification of the ordernumber',tn)) .. '\n' ..
' \n' ..
'QQx1d!QQx11QQx1ba1\n' ..
sl .. "\n" ..
' \n' ..
'QQx1d!QQx00\n' ..
' \n' ..
self:ext_line( t('Details see under',tn) .. ':') .. "\n" ..
self:website() .. '/storagesign\n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
'QQx1dVQQx01\n' ..
'QW\n' ..
'QW\n' ..
'QQx1d@\n'
local requestinfo_table = {}
for zeile in string.gmatch(requestinfo,'[^\n]+') do
table.insert(requestinfo_table,zeile) -- string.sub(zeile,1,#zeile-1))
end
self:send_to_printer(requestinfo_table)
end
--**********************************************************************
function App.xxrequest_flash_signature (self)
math.randomseed(tonumber(self:time())*1000+tonumber(get_timer_value(29)))
if self.flash_is_not_signed == 1 then
self.request_id = math.random(93003,99981)
elseif self.report_is_integer == 0 then
self.request_id = math.random(86003,92981)
end
self.debug1:write(tostring(self.request_id) .. ' ' .. tostring(self.report_is_integer)..' '..tostring(self:time()) )
local sl = tostring(self.request_id) .. trim(tostring(SERIALNUMBER))
sl = self:permutation(1,sl)
sl = 'S ' .. string.sub(sl,1,4) .. ' ' .. string.sub(sl,5,8) .. ' ' .. string.sub(sl,9,12)
-- print(sl)
local requestinfo = {
'',
'',
'QQx1d@',
'QW',
'QW',
'QQx1ba0QQx1d!QQx00',
t('Speichersignierung anfordern'),
t('durch Angabe der Ordernummer:'),
'',
'QQx1d!QQx11QQx1ba1',
sl,
'',
'QQx1d!QQx00',
'',
t('Details siehe unter')..':',
t('www.12park.de/storagesign'),
'',
'',
'',
'',
'QQx1dVQQx01',
'QW',
'QW',
'QQx1d@'
}
self:send_to_printer(requestinfo)
end
--**********************************************************************
function App.print_bestelldaten (self)
local tn = self.lang_nr_for_report
local pin = self.ticketslot_pin:read()
math.randomseed(tonumber(self:time())*1000+tonumber(get_timer_value(29)))
while #pin < 30 do
pin = pin .. tostring(math.random(10002,85997))
end
self.ticketslot_pin:write(pin)
local orderinfo =
' \n' ..
' \n' ..
'QQx1d@\n' ..
'QW\n' ..
'QW\n' ..
'QQx1ba0QQx1d!QQx00\n' ..
self:ext_line( t('Bitte Ticketslot bestellen',tn) ).. "\n" ..
self:ext_line( t('durch Ueberweisung unter',tn) ).. "\n" ..
self:ext_line( t('Angabe einer oder mehrerer',tn) ).. "\n" ..
self:ext_line( t('Ordernummern',tn) .. ":" ) .. "\n" ..
' \n' ..
'QQx1d!QQx11QQx1ba1\n'
while #pin > 4 do
local sl = string.sub(pin,1,5) .. trim(tostring(SERIALNUMBER))
sl = self:permutation(1,sl)
sl = 'T ' .. string.sub(sl,1,4) .. ' ' .. string.sub(sl,5,8) .. ' ' .. string.sub(sl,9,12)
-- print(sl)
orderinfo = orderinfo .. sl .. "\n"
pin = string.sub(pin,6)
end
orderinfo = orderinfo ..
'QQx1d!QQx00\n' ..
self:ext_line( t('Bankverbindung siehe unter',tn) ..":") .. "\n" ..
self:website()..'/ticketslot\n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
'QQx1dVQQx01\n' ..
'QW\n' ..
'QW\n' ..
'QQx1d@\n'
local orderinfo_table = {}
for zeile in string.gmatch(orderinfo,'[^\n]+') do
table.insert(orderinfo_table,zeile) -- string.sub(zeile,1,#zeile-1))
end
self:send_to_printer(orderinfo_table)
end
--**********************************************************************
function App.xxprint_bestelldaten (self)
local pin = self.ticketslot_pin:read()
math.randomseed(tonumber(self:time())*1000+tonumber(get_timer_value(29)))
while #pin < 30 do
pin = pin .. tostring(math.random(10002,85997))
end
self.ticketslot_pin:write(pin)
local orderinfo = {
'',
'',
'QQx1d@',
'QW',
'QW',
'QQx1ba0QQx1d!QQx00',
t('Bitte Ticketslot bestellen'),
t('durch Ueberweisung unter'),
t('Angabe einer oder mehrerer'),
t('Ordernummern:'),
'',
'QQx1d!QQx11QQx1ba1'
}
while #pin > 4 do
local sl = string.sub(pin,1,5) .. trim(tostring(SERIALNUMBER))
sl = self:permutation(1,sl)
sl = 'T ' .. string.sub(sl,1,4) .. ' ' .. string.sub(sl,5,8) .. ' ' .. string.sub(sl,9,12)
-- print(sl)
table.insert(orderinfo,sl)
pin = string.sub(pin,6)
end
table.insert(orderinfo,'QQx1d!QQx00')
table.insert(orderinfo,'')
table.insert(orderinfo,t('Bankverbindung siehe unter:'))
table.insert(orderinfo,self:website()..'/ticketslot')
table.insert(orderinfo,'')
table.insert(orderinfo,'')
table.insert(orderinfo,'')
table.insert(orderinfo,'')
table.insert(orderinfo,'QQx1dVQQx01')
table.insert(orderinfo,'QW')
table.insert(orderinfo,'QW')
table.insert(orderinfo,'QQx1d@')
if file_open == nil then
for i,zeile in ipairs(orderinfo) do
print(zeile)
end
end
self:send_to_printer(orderinfo)
end
--**********************************************************************
function App.website (self)
local flag = FLAGS[self.lang_nr_for_report]
if flag == "AT" then return("www.12park.at") end
return("www.12park.de")
end
--**********************************************************************
function App.send_to_printer (self,printdata,printbuffer)
self.print_data = printdata
print("SEND_TO_PRINTER",type(printdata),#printdata)
self.delete_printbuffer = printbuffer
start_timer(23,10,0,1)
end
--**********************************************************************
function App.print_testausdruck (self)
local sum = math.floor(self:read_counter()/100+0.5)
local sum_format = string.format('%7u',sum)
local pointsalert = 'Points: ' .. sum_format .. '\n'
if sum < 500 then
pointsalert = pointsalert .. ' \n \n \n' .. ' PLEASE UPLOAD POINTS, \n CONTACT 12PARK! \n'
end
local testausdruck1 =
' \n' ..
' \n' ..
'QQx1d@\n' ..
'QW\n' ..
'QW\n' ..
'QQx1ba0QQx1d!QQx00\n' ..
'QQx1d!QQx00\n' ..
' \n' ..
'Test Test \n' ..
' Test Test \n' ..
' Test Te\n' ..
'st Test \n' ..
' Test Test \n' ..
' Test Test\n' ..
' Test \n' ..
' Test Test \n' ..
' Test Test \n' ..
' Test T\n' ..
' \n' ..
' \n' ..
pointsalert -- .. self.comment
local lpatch = ""
if GIT_COMMIT0 ~= GIT_COMMIT then
lpatch = "Patch: " .. GIT_COMMIT .. '\n'
end
if #self.debug3 > 10 then
testausdruck1 = testausdruck1 ..
' \n' ..
' \n' ..
'QQx1d!QQx11QQx1ba1'.."ERROR REPORT".. '\n' ..
'QQx1d!QQx00QQx1ba1Please hand over to 12park\n' ..
string.format("%07u",tonumber(SERIALNUMBER)) .. " " .. GIT_COMMIT0 .. "\n" .. lpatch ..
'QQx1d!QQx00QQx1ba0\n' ..
string.sub(self.debug3,1,1500) .. "\n" ..
' \n'
end
-- local protocolbuffer = nil
feed_the_dog()
local testausdruck2 = ''
if 1==1 then
local protocolbuffer = self:decrypt( pub_key3, my_read_filedata1('printbuffer2.txt',sd_dir..'/printbuffer2.txt') )
if protocolbuffer ~= nil then
testausdruck2 = testausdruck2 .. "\n" ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
'QQx1dVQQx01\n' .. -- paper cut half
protocolbuffer ..
' \n' ..
' \n' ..
'QW\n' ..
'QW\n' ..
'QQx1d@\n'
my_write_filedata1('printbuffer2.txt',sd_dir..'/printbuffer2.txt'," \n",over_write)
end
feed_the_dog()
end
local testausdruck_table = {}
for zeile in string.gmatch(testausdruck1..testausdruck2,'[^\n]+') do
table.insert(testausdruck_table,zeile) -- string.sub(zeile,1,#zeile-1))
end
self.print_data = testausdruck_table
self.delete_printbuffer = 0
start_timer(23,8000,0,1) -- Testausdruck
end
--**********************************************************************
function App.send_to_printer1 (self,text)
local umlaute = nil
if LANGUAGES[lang_nr] == "DE" then
umlaute = 1
end
local zaehler_raw = 0
for i,zeile in ipairs(text) do
-- zeile = zeile:gsub('QQ','\\') .. '\n'
feed_the_dog()
if string.sub(zeile,1,9) ~= '---DEL---' then
zeile = replace_diacritic_letters(zeile,umlaute)
if file_open ~= nil then
if zaehler_raw == 0 then
zeile = zeile .. '\r\n'
else
zaehler_raw = zaehler_raw - 1
end
uart_send_data( self:str_to_byteArray(zeile))
else
print(">>",zeile)
end
else
if string.sub(zeile,10) ~= '' then
zaehler_raw = tonumber( string.sub(zeile,10) )
end
end
-- if i % 50 == 0 then
if i % 9 == 0 then -- dog neu
feed_the_dog()
end
end
-- start_timer(6,1000,0,1)
end
--**********************************************************************
function App.current_screen (self)
-- return self.aktueller_screen
local screen_id = get_current_screen()
local screen = self.screens[screen_id]
-- print("SID",screen_id,screen)
return screen
end
--**********************************************************************
function App.set_visiable (self,x,y,z)
if self.simulation == 0 then
set_visiable(x,y,z)
else
print('set_visiable',x,y,z)
end
end
--**********************************************************************
-- Switch to the call back functions
function App.on_control_notify (self,sid,control,val)
self:on_control_notify1(sid,control,val)
end
--**********************************************************************
function App.on_control_notify1 (self,sid,control,val) local s, r = xpcall(App.p_on_control_notify1,function(e) error1(e) end,self,sid,control,val) return(r) end
--**********************************************************************
function App.p_on_control_notify1 (self,sid,control,val)
if DISABLED == 1 then
return
end
local screen = self.screens[sid]
-- print('ON_CONTROL',sid,control,val)
stop_timer(25) --- Unterbreche die
stop_timer(26) --- Ticket Report Berechnung
self.generate_report_thread = nil
if control == 95 and (screen == self.standby or screen == self.lang or screen == self.adm_device) then
-- print('XXXX',sid,screen,self.adm_device,control)
-- if self.doubleklick ~= screen then
if val == 1 then
-- self:press_action()
-- start_timer(21,1,0,1)
-- print("WWW1",screen)
self.doubleklick = screen
self:on_uart_recv_data( { [0] = 0xEE, [1] = 0xB1, [2] = 21 } )
else
-- self:release_action()
-- start_timer(22,1,0,1)
-- print("WWW2",screen)
self.doubleklick = nil
self:on_uart_recv_data( { [0] = 0xEE, [1] = 0xB1, [2] = 22 } )
end
return
end
local long_press = self.long_press
self.long_press = 0
if control == 95 or control == 96 or control == 4 and screen == self.coin then
control = 80
end
if control == 94 and screen == self.lang then
return self:press_knob(screen,val,long_press)
end
self:renew_timeout()
if control == 80 then
return self:press_knob(screen,val,long_press)
elseif control > 80 and control < 89 then
return self:insert_coin(control-80+1)
elseif control > 1080 and control < 1089 then
return self:insert_coin(control-1070+1)
elseif screen == self.lang and control == 7 then
self.long_press = 1
self:on_control_notify1(sid,80)
elseif screen == self.lang and control > 0 and control < 4 then
self:admin_bereich(control)
self:switch_language(control)
elseif screen == self.pin then
return self:pin_eingabe(control)
elseif screen == self.ticketslot and control == 10 then
self:activate_ticketslot()
-- rem_projekt_vm
elseif screen == self.ticketslot and control == 7 then
print(1235,self.flash_is_not_signed,self.report_is_integer)
if self.machine_is_running == 1 then
-- if self.flash_is_not_signed == 0 and self.report_is_integer == 1
-- or self.report_is_integer == 2 then -- ist nicht integer, sondern weggedrueckt
self.ticketslot_start = 0
self:print_bestelldaten()
else
self:request_flash_signature() -- anfordern einer neuen Signatur durch Ausdruck
-- self:print_bestelldaten()
end
elseif screen == self.ticketslot and control == 17 then
if file_open == nil then
self:disable_device()
else
if self.machine_is_running == 1 then
-- if (self.flash_is_not_signed == 1) or not (self.report_is_integer == 0) then
-- if 0 == 0 then -- self.ticketslot_start == 0 then
self.adm_ext:activate()
else
self:disable_device()
end
end
elseif screen == self.adm_basic and control == 99 then
self.admin:activate()
elseif screen == self.adm_basic and 29 < control and control < 33 then
self.einzustellende_sprache = control
self.adm_lang_sel:activate()
elseif screen == self.admin and control == 12 then
self:check_if_tarif2_is_necessary()
self:make_tarif_table()
self.adm_tarif:activate()
elseif screen == self.admin and control == 2 then
self.adm_basic:activate()
elseif screen == self.admin and control == 13 then
self.report_wird_gedruckt:set_visiable(0)
self.press_protocol = 0
self.adm_proto:activate()
elseif screen == self.admin and control == 5 then
self.adm_ext:activate()
elseif screen == self.adm_proto and control == 13 then
self.press_protocol = self.press_protocol + 1
self:print_report()
elseif screen == self.adm_ext and control == 1 then
-- self.ticketslot_pin:write( tostring(math.random(10002,89997)) )
self.ticketslot:activate()
elseif screen == self.admin and control == 99 then
self.no_timeout = 0
self.who_logged_in_as_admin = '' -- den aktuellen Benutzer abmelden
self:compute_shortinfo()
self:log("logged out ...")
self.normal_test1 = "1432"
local reports3 = self:read_report()
self.normal_test = "1334"
error2("wr051")
self:write_report(reports3,self:date("%y%m%d%H%M%S").."X","0,xx")
error2("wr052")
self:zeitumstellung()
set_backlight(self.BLACKSCREEN)
self:update_pruefcode()
-- self:check_for_nightmode()
self.standby:activate()
elseif screen == self.adm_lang_sel then
if control == 99 then
self.adm_basic:activate()
else
self:sprachauswahl(control)
end
elseif screen == self.adm_ext and control == 12 then
-- print("ADMIN",self.who_logged_in_as_admin)
if self.who_logged_in_as_admin ~= '' then
if string.sub(self.who_logged_in_as_admin,1,1) == '.' then -- Admin-Zugriffe mit Namen, der mit Punkt beginnt,
self.adm_ext:activate() -- sind nicht erlaubt, die PIN-Liste zu
return 'X' -- lesen oder schreiben
end
end
self:pin_base(control)
elseif screen == self.adm_ext and control == 3 then
if self.who_logged_in_as_admin ~= '' then
if string.sub(self.who_logged_in_as_admin,1,1) == '.' then -- Admin-Zugriffe mit Namen, der mit Punkt beginnt,
self.adm_ext:activate() -- sind nicht erlaubt, die PIN-Liste zu
return 'X' -- lesen oder schreiben
end -- (Code ist von oben kopiert, sorry)
end
self.adm_pin_new:activate()
elseif screen == self.adm_ext and control == 99 then
self.admin:activate()
elseif screen == self.adm_ext and control == 8 then
self.adm_device:activate()
elseif screen == self.adm_device and control == 99 then
self.adm_ext:activate()
elseif screen == self.adm_pin_new and control == 5 then
self.admin:activate()
elseif screen == self.adm_pin_new and control == 17 then
self:pin_list_change()
self:pin_base(12)
elseif screen == self.adm_pin_new and control == 99 then
self.adm_ext:activate()
elseif screen == self.pin_admin then
self:pin_base(control)
elseif screen == self.qrcode and (control == 2 or control == 93) then
self:aktiviere_qr_tastenfeld(1)
elseif screen == self.qrcode and control > 9 and control < 21 and val == 1 then
self:press_qr_taste(control)
elseif screen == self.qrcode and control == 94 then
self:renew_qr_timeout()
self.no_timeout = 0
elseif screen == self.qrcode and control == 99 then
stop_timer(18)
stop_timer(3)
self:on_timer(2)
elseif screen == self.standby and control == 99 then
return self:admin_bereich()
elseif screen == self.adm_tarif and control == 20 then
self:make_tarif_table()
return self.adm_tarif2:activate()
elseif screen == self.adm_tarif and control == 4 then
return self.adm_tarif_def:activate()
elseif screen == self.adm_tarif2 and control == 4 then
return self.adm_tarif_def2:activate()
elseif screen == self.adm_tarif_def and control == 99 then
self:make_tarif_table()
return self.adm_tarif:activate()
elseif screen == self.adm_tarif_def2 and control == 99 then
self:make_tarif_table()
return self.adm_tarif2:activate()
elseif screen == self.adm_tarif and control == 19 then
self:make_tarif_table()
return self.adm_tarif2:activate()
-- buttons:
elseif screen == self.adm_tarif and control == 17 then
local x = tonumber(self.tagesticket[1]:read())
self.tagesticket[1]:write(tostring( (x+1)%2 ))
print(self.tagesticket[1]:read(),1236)
self:synchronize_buttons()
elseif screen == self.adm_tarif2 and control == 17 then
local x = tonumber(self.tagesticket[2]:read())
self.tagesticket[2]:write(tostring( (x+1)%2 ))
self:synchronize_buttons()
elseif screen == self.adm_tarif and control == 3 then
local x = tonumber(self.pay_mode[1]:read())
self.pay_mode[1]:write(tostring( (x+1)%3 ))
self:synchronize_buttons()
elseif screen == self.adm_tarif2 and control == 3 then
local x = tonumber(self.pay_mode[2]:read())
self.pay_mode[2]:write(tostring( (x+1)%3 ))
self:synchronize_buttons()
elseif screen == self.adm_device and control == 17 then
local x = self.reset_device
-- print("DD1",x)
self.reset_device = (x+1)%2
-- print("DD2",self.reset_device)
self:synchronize_buttons()
else
for i,field in ipairs(self.fields) do
if field:synchronize_with_entry(screen,control) == 1 then
break
end
end
end
--
end
-- cf1 = load(chunk1) cf1()
--**********************************************************************
function App.aktiviere_qr_tastenfeld (self,mode)
self:renew_qr_timeout()
for nr = 1,12 do
self.qrtaste[nr]:set_visiable(mode)
end
end
--**********************************************************************
function App.press_qr_taste (self,nr)
self:renew_qr_timeout()
local tan_part
if nr < 20 then
tan_part = self.qrtan:read()
tan_part = tan_part .. string.format("%1u",nr-10)
self.qrtan:write(tan_part)
elseif nr == 20 then
tan_part = self.qrtan:read()
tan_part = string.sub(tan_part,1,#tan_part-1)
self.qrtan:write(tan_part)
end
if #tan_part == 5 then
-- print("TT",tan_part)
local sbed = #self.falsche_qr_eingabe > 1 and self.falsche_qr_eingabe[1] == "15970"
and self.falsche_qr_eingabe[2] == "15463"
if self:check_tan(1,tostring(self.qr_ordernr),tostring(self.qrtan:read())) == 1 or sbed then
self.nochmal:set_visiable(-1)
self.nochmal_button:set_visiable(-1)
self.qr_ordernr = nil
-- self.coin:activate()
self.no_timeout = 0
stop_timer(18)
self:renew_timeout()
start_timer(7,7,0,1)
else
table.insert(self.falsche_qr_eingabe,self.qrtan:read())
-- self.qrtan:write("")
if #self.falsche_qr_eingabe > 7 then
self.start_test_nr = ""
self:on_timer(18)
elseif string.sub(self.qrtan:read(),1,1) == "0" then
self.start_test_nr = self.qrtan:read()
self:on_timer(18)
else
self.qr_invalid:set_visiable(1)
start_timer(19,1700,0,1)
self.no_timeout = 0
end
end
end
end
--**********************************************************************
function App.compute_shortinfo (self)
local pph = self:get_price_per_hour(1)
local ttt = self.tagesticket[1]:read()
local dauer = math.abs( self.max_park_hour[1]:read() )
local d_min = string.format('%02u',math.abs( tonumber(self.max_park_minute[1]:read())))
local minp = self.min_sum[1]:read()
if self.tarif_table[1] == nil then
local text = pph .. ' ' .. CURRENT .. ' ' .. t('pro Stunde') .. ', '
if tonumber(minp) > 0.00 then
text = text .. t('mind.') .. ' ' .. tostring(minp):gsub('%.',',') .. ' ' .. CURRENT .. ', '
end
if ttt == '0' then
text = text .. t('Höchstparkdauer') .. ' ' .. dauer .. ':' .. d_min .. ' ' .. t('Std.')
else
text = text .. t('Tagesticket ab') .. ' ' .. dauer .. ' ' .. t('Std.')
end
self.shortinfo:write(text)
else
self.shortinfo:write("")
end
end
--**********************************************************************
function App.press_knob (self,screen,val,long_press)
local screen = self:current_screen()
-- print('RRW',screen.adm_device,screen,val,long_press)
if false and self:current_screen() == self.standby then local f = 1 -- UART Test wenn true
elseif screen == self.lang then
if (long_press == 1) then
self.pin_mode = 'lang'
self.ungueltig = 0
self.aktuelle_pin_eingabe = ''
if self.press_release == 1 then -- Sprache zurueckstellen bei Press und Press_long
if self:switch_language() == 3 then
self:switch_language()
end
end
if self.broetchen == 1 then
self.show_text_muenzen = 1
self:activate_sondertarif(1)
elseif self.wohnmobil == 1 then
self.show_text_muenzen = 1
self:activate_sondertarif(0)
else
if string.find( self:read_pin_defs(),',E' ) then
self.pin:activate()
end
end
else
self:switch_language()
end
elseif screen == self.coin then
self:insert_coin(1)
elseif screen == self.standby then
if long_press == 1 then
self:admin_bereich()
end
if self.real_coins == 0 then
self:reset_coin()
end
if self.anzeige_betrag:read() == '0,00' then
-- if self:switch_language(1) == 99199 then
-- self.coin:activate()
-- else
self:switch_language(1)
self:choose_flags()
self.lang:activate()
-- end
else
self.coin:activate()
end
elseif screen == self.adm_device then
if long_press == 1 then
self:check_for_flash_reset( trim(self.reset_ok_code:read()) )
end
end
end
--**********************************************************************
function App.choose_flags (self)
-- print(self.lang_selector)
local cc = self.lang_selector:read()
if self.flags_set == cc then
return
end
local cc1 = string.sub(cc,1,2)
local cc2 = string.sub(cc,3,4)
local cc3 = string.sub(cc,5,6)
-- print("CHHOSE_FLAGS",cc1,cc2,cc3)
for lang,lfield in pairs(self.lang1) do
-- print(lang,lfield)
lfield:set_visiable( (cc1==lang) and 1 or 0 )
end
for lang,lfield in pairs(self.lang2) do
lfield:set_visiable( (cc2==lang) and 1 or 0 )
end
for lang,lfield in pairs(self.lang3) do
lfield:set_visiable( (cc3==lang) and 1 or 0 )
end
if cc1 ~= nil then self.lang_description1:write( self.ldescriptions[ self:language_number(cc1) ] or '' ) end
if cc2 ~= nil then self.lang_description2:write( self.ldescriptions[ self:language_number(cc2) ] or '' ) end
if cc3 ~= nil then self.lang_description3:write( self.ldescriptions[ self:language_number(cc3) ] or '' ) end
end
--**********************************************************************
function App.admin_bereich (self,control)
-- print('11',control,self.admin_count,self.control0)
self.press_protocol = 0
-- print("CCC",self.admin_count,self.control0,control)
if control ~= nil and self.admin_count == 0 or self.control0 ~= nil and self.control0 ~= control then
self.admin_count = 0
stop_timer(4)
self.control0 = nil
return
end
self.control0 = control
if self.admin_count == 0 then
start_timer(4,8000,0,1)
end
self.admin_count = self.admin_count + 1
-- print('12',control,self.admin_count,self.control0)
if self.admin_count > 4 then
self.pin_mode = 'admin'
self.admin_count = 0
self.pin:activate()
end
end
--**********************************************************************
function App.pin_eingabe (self,control)
print('PIN'..tostring(control))
print('AKT'..self.aktuelle_pin_eingabe)
if self.ungueltig == nil then
self.ungueltig = 0
end
if self.pin_wartezeit > 0 then
return
end
if control == 14 then
if self.pin_feld:read() == "" then
self.no_timeout = 0
set_backlight(self.BLACKSCREEN)
self.standby:activate()
else
self.aktuelle_pin_eingabe = ''
self.pin_feld:write('')
end
return
end
if control > 3 and control < 14 then
self.aktuelle_pin_eingabe = self.aktuelle_pin_eingabe .. tostring((control-3)%10)
if #self.aktuelle_pin_eingabe == 1 then self.pin_feld:write('*')
elseif #self.aktuelle_pin_eingabe == 2 then self.pin_feld:write('* *')
elseif #self.aktuelle_pin_eingabe == 3 then self.pin_feld:write('* * *')
elseif #self.aktuelle_pin_eingabe == 4 then self.pin_feld:write('* * * *')
elseif #self.aktuelle_pin_eingabe == 5 then self.pin_feld:write('* * * * *')
end
if #self.aktuelle_pin_eingabe == 5 then
local erg = self:pruefe_pin(self.aktuelle_pin_eingabe)
-- print("RRR",self.aktuelle_pin_eingabe)
-- local u_key = tostring(self.aktuelle_pin_eingabe) .. "," .. tostring(math.ceil(tonumber(self:time())/86400))
-- print("U_KEY",u_key,self.used_pins[u_key],self.anzahl_dieselbe_pin)
-- if self.used_pins[u_key] ~= nil and self.used_pins[u_key] >= self.anzahl_dieselbe_pin then
-- erg = nil -- ist an diesem Tag schon einmal aufgerufen worden
-- end
local group_key = tostring(self.e_gruppen_id)
local group_datum = tostring(math.ceil(tonumber(self:time())/86400))
if not (self.datum_aktuelle_ticketliste == group_datum) then -- es handelt sich offenbar um einen neuen Tag, daher die Liste der verbrauchten Tickets neu anlegen
self.used_pin_groups = {}
self.datum_aktuelle_ticketliste = group_datum
end
-- print("PPPP",self.used_pin_groups[group_key])
local used_zaehler = 0
if self.used_pin_groups[group_key] ~= nil then
local akt_zeit_hm = self:date('%H%M',akt_time)
for i,pin_request in ipairs(self.used_pin_groups[group_key]) do
if tonumber(pin_request) > tonumber(akt_zeit_hm) then
used_zaehler = used_zaehler + 1
end
end
end
-- print('ERG',erg,used_zaehler,self.anzahl_dieselbe_pin)
stop_timer(5)
self.ungueltig = self.ungueltig + 1
if self.pin_mode == 'lang' then
if erg == 'E' and used_zaehler < self.anzahl_dieselbe_pin then
-- self.aktuelle_pin_eingabe = ''
self:activate_sondertarif(0)
end
elseif self.pin_mode == 'admin' then
if erg == 'A' or erg == 'S' then
self.ungueltig = 0
self.no_timeout = 1
self:log( "Admin logged in " .. self.aktuelle_pin_eingabe )
local aktpin = self.aktuelle_pin_eingabe
aktpin = string.gsub(aktpin,"1","l")
aktpin = string.gsub(aktpin,"2","z")
aktpin = string.gsub(aktpin,"3","e")
aktpin = string.gsub(aktpin,"4","t")
aktpin = string.gsub(aktpin,"5","s")
aktpin = string.gsub(aktpin,"6","b")
aktpin = string.gsub(aktpin,"7","v")
aktpin = string.gsub(aktpin,"8","w")
aktpin = string.gsub(aktpin,"9","q")
aktpin = string.gsub(aktpin,"0","o")
self.normal_test1 = "1433"
local reports5 = self:read_report()
self.normal_test = "1335"
error2("wr053")
self:write_report(reports5,self:date("%y%m%d%H%M%S").."X","0,"..string.sub(aktpin,1,2))
error2("wr054")
self.aktuelle_pin_eingabe = ''
-- self:renew_timeout()
self.admin:activate()
end
end
self.pin_feld:write('')
if self.ungueltig > 0 then
self.aktuelle_pin_eingabe = ''
self.pin_wartezeit = 2000
if self.ungueltig == 1 then self.pin_wartezeit = 100
elseif self.ungueltig == 2 then self.pin_wartezeit = 100
elseif self.ungueltig == 3 then self.pin_wartezeit = 200
elseif self.ungueltig == 4 then self.pin_wartezeit = 300
elseif self.ungueltig == 5 then self.pin_wartezeit = 400
elseif self.ungueltig == 6 then self.pin_wartezeit = 600
elseif self.ungueltig == 7 then self.pin_wartezeit = 1000
end
self.pin_feld:write('***********')
if erg == 'E' and used_zaehler >= self.anzahl_dieselbe_pin then
self.ungueltig_text:write(t("Nicht verfügbar"))
else
self.ungueltig_text:write(t("Ungültige PIN"))
end
self.ungueltige_pin:set_visiable(0)
start_timer(5,self.pin_wartezeit+1000,0,1)
end
end
end
end
--**********************************************************************
function App.activate_sondertarif (self,nr)
self.ungueltig = 0
if self.broetchen == 0 then
self.sondertarif_anzeige:set_visiable(0)
end
self.tarifnr = 2
self.anzeige_parkdauer:write(self:date(t('ZEIT')))
if tonumber(self.pay_mode[2]:read()) > 0 then
self.muenzen:set_visiable(1)
self.muenzen1:set_visiable(1)
self.muenzen2:set_visiable(1)
self.muenzen3:set_visiable(1)
self.bargeldlos:set_visiable(1)
else
self.muenzen:set_visiable(-1)
self.muenzen1:set_visiable(-1)
self.muenzen2:set_visiable(-1)
self.muenzen3:set_visiable(-1)
self.bargeldlos:set_visiable(-1)
end
self:insert_coin(nr)
end
--**********************************************************************
function App.pruefe_pin (self,pin)
-- print(pin,self.superpin)
-- if pin == self.superpin then
-- return 'S'
-- end
-- if pin == '99987' then @@ for test
-- return 'E'
-- end
self.anzahl_dieselbe_pin = 99999999
local pin_defs = self:read_pin_defs()
print("PIN_DEFS",pin_defs)
local x1,x2 = string.find(pin_defs,',.'..pin..'.-,')
if x1 ~= nil then -- Eintrag gefunden
local mode = string.sub(pin_defs,x1+1,x1+1)
local remark = string.sub(pin_defs,x1+7,x2-1)
if trim(mode) == '' and remark ~= nil and #remark > 0 then
mode = 'E'
end
-- 1. erstmal die Vielfachheit der Ticketausgabe auslesen
if mode == 'E' then
self.anzahl_dieselbe_pin = 1
local x1 = 1
local x2 = 1
x1,x2 = string.find(remark,"%/%d+[%+=#x]%/")
if x1 ~= nil and x2 ~= nil then
x2 = string.sub(remark,x1+1,x2-2)
self.anzahl_dieselbe_pin = fcheck(1,tonumber,x2)
if self.anzahl_dieselbe_pin == nil then
self.anzahl_dieselbe_pin = 1
end
end
-- 2. jetzt die Gruppenzugehoerigkeit rauslesen
self.e_gruppen_id = pin
x1 = 1
x2 = 1
x1,x2 = string.find(remark,"%/%@[^%/]+%/")
if x1 ~= nil and x2 ~= nil then
self.e_gruppen_id = string.sub(remark,x1+2,x2-1)
end
-- 3. Intervalle fuer Freitickets auslesen
self.freiticket_interval = {"0000","2359"}
x1 = 1
x2 = 1
x1,x2 = string.find(remark,"%/[0123456789,]+%/")
if x1 ~= nil and x2 ~= nil then
local f_int = string.sub(remark,x1+1,x2-1)
self.freiticket_interval = {"0000"}
local value_before = 0
local zeile = ""
for zeile in string.gmatch(f_int,'[^,]+') do
-- print("ZZZ",zeile)
if #zeile == 1 then
zeile = "0" .. zeile + "00"
end
if #zeile == 2 then
zeile = zeile .. "00"
end
if #zeile == 3 then
zeile = "0" .. zeile
end
if #zeile == 4 then
if tonumber(zeile) > value_before and tonumber(zeile) < 2359 then
value_before = tonumber(zeile)
table.insert(self.freiticket_interval,zeile)
end
end
end
table.insert(self.freiticket_interval,"2359")
end
-- 4. Sondertarif_text auslesen
self.sondertarif_text = ""
x1 = 1
x2 = 1
x1,x2 = string.find(remark,"%/%.[^%/]+%/")
if x1 ~= nil and x2 ~= nil then
self.sondertarif_text = string.sub(remark,x1+2,x2-1)
end
if self.sondertarif_text == "" then
x1 = 1
x2 = 1
x1,x2 = string.find(remark,"%/[^%@%d][^%/]+%/")
if x1 ~= nil and x2 ~= nil then
self.sondertarif_text = string.sub(remark,x1+1,x2-1)
end
end
print("PARAM",self.anzahl_dieselbe_pin,self.e_gruppen_id,self.freiticket_interval,self.sondertarif_text)
end
if mode == 'A' then
self.who_logged_in_as_admin = remark
end
if mode == 'E' then
self.last_entered_pin = remark
end
return mode
end
-- Die Pruefung in der PIN-Liste hat keinen entsprechenden Eintrag gefunden, jetzt also gegen die Super-Pin pruefen
local s1 = '' -- Superpin aus der SERIALNUMBER herstellen und dagegen pruefen
local s2 = ''
local sernr = tostring(SERIALNUMBER)
while #sernr > 0 do
local x = string.sub(sernr,1,1)
sernr = string.sub(sernr,2)
if #s1 == 0 or tonumber(x) > tonumber(string.sub(s1,#s1,#s1)) then
s1 = s1 .. x
else
s2 = s2 .. x
end
end
if tostring(pin) == string.sub(s1..s2,1,5) then
x1,x2 = string.find(pin_defs,',A%d%d%d%d%d[^%.]') -- Suche nach einem gesetzten Admin-Eintrag, der auch PIN-Zugang erlaubt
local zufallswert = self:time() % 6 -- bei jedem 6.ten Mal durchlassen, also muss man schon oefter probieren, nicht so einfach, mit der Super-PIN einzuloggen
print("ZUFALLSWERT:",zufallswert)
if x1 == nil or zufallswert == 1 then -- PIN-Verwaltung gibt, dann muss man mehrfach versuchen mit dem Standard-Passwort
if x1 ~= nil then -- es gibt Admin-Pins, und der Zugang wurde jetzt durch einige Versuche durch die normale PIN geknackt
while 0 == 0 do -- daher jetzt im Nachgang alle Admin-PINs sukzessive loeschen, als Sanktion
x1,x2 = string.find(pin_defs,',A.-,')
if x1 == nil then -- die Loesch-Arbeit ist getan, jetzt raus
break
else
local old_text = string.sub(pin_defs,x1,x2) -- den gefundenen Eintrag loeschen
pin_defs = string.gsub(pin_defs,old_text,',,')
end
end
self.pin_definitions:write(pin_defs) -- die um Admin-Pins gesaeuberte Liste in den Speicher schreiben
end
return 'S'
end
end
end
--**********************************************************************
function App.sprachauswahl (self,control)
local sprache = self.adm_lang_sel:get_text(control+20)
-- if string.sub(sprache,1,1) == " " then
-- return
-- end
local lang_act_number = 1
if string.sub(self.lang_selector:read(),1,2) == self.country_table[lang_nr] then
lang_act_number = 1
elseif string.sub(self.lang_selector:read(),3,4) == self.country_table[lang_nr] then
lang_act_number = 2
elseif string.sub(self.lang_selector:read(),5,6) == self.country_table[lang_nr] then
lang_act_number = 3
end
if self.einzustellende_sprache == 30 then
self.setup_lang1:write(sprache)
elseif self.einzustellende_sprache == 31 then
self.setup_lang2:write(sprache)
elseif self.einzustellende_sprache == 32 then
self.setup_lang3:write(sprache)
end
local langs = self.setup_lang1:read() .. self.setup_lang2:read() .. self.setup_lang3:read()
-- print('LL',langs)
langs = langs:gsub('-','')
langs = langs:gsub('X','')
langs = langs:gsub(' ','')
if #langs == 0 then
self.adm_basic:set_text(20,self.lang_selector:read())
else
if string.sub(langs,1,2) == string.sub(langs,3,4) then -- wenn alle drei Sprachen im Auswahlmenue gleich eingestellt sind
if string.sub(langs,1,2) == string.sub(langs,5,6) then -- dann wird die Sprache fuer die Reports auf diese Sprache gestellt
self.reportlang:write(string.sub(langs,1,2) ) -- (Default ist: die erste Sprache im lang_selector).
end -- Danach sollte man sofort wieder umstellen auf unterschiedliche Sprachen.
end
self.lang_selector:write(langs)
end
self:set_lang_nr_for_report()
self:switch_language(lang_act_number)
for sid,screen in pairs(self.screens) do
screen.act_lang = -1
end
self.adm_basic:activate()
end
--**********************************************************************
function App.switch_language (self,lang)
local langs = self.lang_selector:read()
local anz = #langs/2
-- print("LANG",lang)
-- if lang == 9 then return anz end
if anz == 1 then
self.lang = self.lang_layout1
elseif anz == 2 then
self.lang = self.lang_layout2
elseif anz == 3 then
self.lang = self.lang_layout3
end
-- 1. erstmal die Sprache waehlen und highlighten
if lang ~= nil then
-- print('new lang',control)
set_value( self.lang.screen_id,1, 0 )
set_value( self.lang.screen_id,2, 0 )
set_value( self.lang.screen_id,3, 0 )
set_value( self.lang.screen_id,lang, 1 )
else
-- print('ANZ',anz,x)
local x = get_value( self.lang.screen_id,anz)
set_value( self.lang.screen_id, 3, get_value( self.lang.screen_id,2) )
set_value( self.lang.screen_id, 2, get_value( self.lang.screen_id,1) )
set_value( self.lang.screen_id, 1, x )
-- print (get_value(2,1), get_value(2,2), get_value(2,3), '----')
end
-- 2. daraus den gewaehlten Laendercode herausfinden
local actual_country = ( get_value( self.lang.screen_id,1 ) == 1 and string.sub(langs,1,2) or '' ) ..
( get_value( self.lang.screen_id,2 ) == 1 and string.sub(langs,3,4) or '' ) ..
( get_value( self.lang.screen_id,3 ) == 1 and string.sub(langs,5,6) or '' )
-- 3. daraus die Sprache herausfinden
local actual_language = 999
for i,country in ipairs(self.country_table) do
if self.country_table[i] == actual_country then
actual_language = self.ldescriptions[i]
end
end
-- 4. daraus die Sprachnummer in der Translations-Tabelle herausfinden
local nr = 1
while nr < 1000 do
local try_the_language = t("LANG",nr)
if try_the_language == "" then
break
end
if try_the_language == actual_language then
lang_nr = nr
break
end
nr = nr + 1
end
-- set_language(langcode-1)
-- lang_nr = langcode + 1
-- 5. den aktuellen Screen gleich an die neue Sprache anpassen
local screen1 = self:current_screen()
-- print("SC",screen1,screen1.screen_id)
screen1:activate("force")
self:compute_shortinfo()
return anz
end
--**********************************************************************
function App.language_number (self,langcode)
local actual_lang_nr = self.cache_langcode_to_nr[langcode]
if actual_lang_nr == nil then
for i,lang in ipairs(self.country_table) do
if lang == langcode then
self.cache_langcode_to_nr[langcode] = i
break
end
end
actual_lang_nr = self.cache_langcode_to_nr[langcode]
end
return actual_lang_nr or nil
end
--**********************************************************************
function App.insert_coin (self,nr) -- can only be entered when screen = self.coin
if self.ticket_wird_gerade_gedruckt == 1 then
if nr / 10 > 1 then -- nur bei Muenzen
if self:read_counter() > 0 then
stop_timer(2)
self:reset_coin()
self.nr_coin = nr
start_timer(28,500,0,1)
-- self:insert_coin(nr)
end
end
return
end
self.coin:activate()
self.nochmal:set_visiable(-1)
self.nochmal_button:set_visiable(-1)
self.who_logged_in_as_admin = ''
-- print("INSERT COIN INFO",self.real_coins,self.hoechstparkdauer_ist_erreicht,nr)
if nr > 10 then -- eine reale Muenze wurde eingeworfen
nr = nr - 10
if self.real_coins == 0 then
self.anzeige_betrag:write('0,00')
self.muenzen:set_visiable(-1)
self.muenzen1:set_visiable(-1)
self.muenzen2:set_visiable(-1)
self.muenzen3:set_visiable(-1)
self.bargeldlos:set_visiable(-1)
self.real_coins = 1
end
end
-- print("NR",nr)
-- hh = "x" + 1 -- Test Fehler
if self.real_coins == 0 and self.hoechstparkdauer_ist_erreicht == 1 and nr > 1 then
return
end
-- print( tostring(string.gsub(self.anzeige_betrag:read(),',','.' )) )
self.amount_in_euro = tonumber( tostring(self.anzeige_betrag:read():gsub(',','.')) )
if nr > 0 then
self.amount_in_euro = self.amount_in_euro + COINVALUE[nr]
end
self.anzeige_betrag:write( string.format('%5.2f',self.amount_in_euro):gsub('%.',',') )
self.mwst = fcheck(0,tonumber,self.tax_vat:read())
if self.mwst == nil then
self.mwst = 0
end
self.umsatzsteuer = self.amount_in_euro * self.mwst / (100 + self.mwst)
-- print('MWST',self.tax_vat:read(),mwst,self.umsatzsteuer)
self.akt_time = self:time() + 35
local parkdauer_in_sec = self:compute_parkdauer()
-- print("NNN",self.nightmode,self.morgensec,self.abendsec)
-- if self.nightmode ~= "" then
-- local abend = 86400 * (math.floor((self.akt_time-self.morgensec+7200)/86400))
-- morgen = (abend + 86400) + self.morgensec
-- abend = abend + self.abendsec
-- print("AAM",abend-1730000000,self.akt_time+parkdauer_in_sec-1730000000,morgen-1730000000)
-- if (self.akt_time + parkdauer_in_sec) > abend then
-- self.amount_in_euro = self.amount_in_euro - tonumber(string.sub(self.nightmode,9,12))*0.01 -- fiktiver Preis ohne Nachtgebuehr
-- if (self.amount_in_euro >= 0) then
-- local parkdauer_in_sec1 = self:compute_parkdauer()
-- print("TTS",self.amount_in_euro,parkdauer_in_sec1,self.akt_time+parkdauer_in_sec1-1730000000)
-- if (self.akt_time + parkdauer_in_sec1) > abend then
-- print("MM",morgen,self.akt_time)
-- parkdauer_in_sec = morgen - self.akt_time
-- end
-- end
-- self.amount_in_euro = self.amount_in_euro + tonumber(string.sub(self.nightmode,9,12)) -- Rueckkorrektur
-- end
-- print("PIS",parkdauer_in_sec)
-- end
local hoechstparkdauer = tonumber(self.max_park_hour[self.tarifnr]:read()) * 3600
+ tonumber(self.max_park_minute[self.tarifnr]:read()) * 60
if hoechstparkdauer < 0 then
self.ueberzahlung_ausweisen = 1
hoechstparkdauer = - hoechstparkdauer
else
self.ueberzahlung_ausweisen = 0
end
-- print("HH",hoechstparkdauer)
self.parkzeitende_sec = math.ceil(self.akt_time + parkdauer_in_sec)
local is_tagesticket = self.tagesticket[self.tarifnr]:read()
-- if tonumber(is_tagesticket) == 1 then
-- hoechstparkdauer = math.min(hoechstparkdauer,math.ceil(akt_time/86400) * 86400
-- - akt_time - 40) -- Hoechstparkzeit auf 23:59 heutiger Tag begrenzen
-- end
-- print(is_tagesticket,hoechstparkdauer,self.parkzeitende_sec,self.akt_time)
self.debug1:write("HB " .. tostring(self.hoechstbetrag[1]) .. " " .. tostring(self.hoechstbetrag[2]))
self.hour = -1
self.min = -1
-- local zusatztage = math.floor(self.parkzeitende_sec/86400) - math.floor(self.akt_time/86400) -- kann wieder untern bleiben wg geandertem Algorithmus
if self.parkzeitende_sec >= self.akt_time + hoechstparkdauer then
-- print("DDDDDD",self.akt_time,self.parkzeitende_sec,hoechstparkdauer,116)
self.hoechstparkdauer_ist_erreicht = 1
self.parkzeitende_sec = self.akt_time + hoechstparkdauer
self.parkzeitende_hm = self:date(t('ZEIT'),self.parkzeitende_sec)
self.parkzeitende_day = self:date(t('DATUM'),self.parkzeitende_sec)
parkdauer_in_sec = hoechstparkdauer
if tonumber(is_tagesticket) == 1 then
self.parkzeitende_tagesticket = "2359"
if self.tarifnr == 2 then -- im Tarif 2 kann das "Tagesticket" in kleinere Tagesintervalle eingeteilt sein
local akt_zeit_hm = self:date('%H%M',self.akt_time)
for i,f_intval in ipairs(self.freiticket_interval) do
if tonumber(akt_zeit_hm) < tonumber(f_intval) then
if tonumber(f_intval) < tonumber(self.parkzeitende_tagesticket) then
self.parkzeitende_tagesticket = f_intval
end
end
end
end
local parkzeitende_h = tonumber(string.sub(self.parkzeitende_tagesticket,1,2))
local parkzeitende_m = tonumber(string.sub(self.parkzeitende_tagesticket,3,4))
self.parkzeitende_hm = self:date(t('ZEIT'),parkzeitende_h*3600+parkzeitende_m*60) -- AAAAAAAAAA
self.parkzeitende_day = self:date(t('DATUM'),self.akt_time) -- + 86400*zusatztage) -- Alogorithmus geaendert: Tagesticket immer gleicher Tag
local akt_hm = self:date("%H%M",self.akt_time)
self.hour = parkzeitende_h - tonumber(string.sub(akt_hm,1,2))
self.min = parkzeitende_m - tonumber(string.sub(akt_hm,3,4))
-- print("TTT",self.hour,self.min,self.parkzeitende_tagesticket,akt_hm)
if self.min < 0 then
self.min = self.min + 60
self.hour = self.hour - 1
end
parkdauer_in_sec = self.hour*3600 + self.min*60
-- parkdauer_in_sec = ( math.ceil(akt_time/86400) * 86400 - akt_time ) - (1440 - parkzeitende_h*60 - parkzeitende_m)*60 + 20
end
else
self.parkzeitende_hm = self:date(t('ZEIT'),self.parkzeitende_sec)
self.parkzeitende_day = self:date(t('DATUM'),self.parkzeitende_sec)
end
if self.parkzeitende_sec >= self.akt_time + hoechstparkdauer - self.korrektur_hoechstparkdauer then
self.hoechstparkdauer_ist_erreicht = 1
end
-- print('R',parkdauer_in_sec,self.amount_in_euro)
-- local tag_ist_zu_ende = 0
-- if self:date(t('DATUM')) ~= self:date(t('DATUM'),self.park_bis_in_sec) and self.is_tagesticket == '1' then
-- self.park_bis_hm = t('23:59')
-- self.park_bis_day = self:date(t('DATUM'))
-- tag_ist_zu_ende = 1
-- end
-- print('WWW',park_bis_in_sec,park_bis_hm,'---------------------------')
local zusatztage = math.floor(self.parkzeitende_sec/86400) - math.floor(self.akt_time/86400) -- ist nach oben gegangen wg Bem No. 7
self.ticketanzahl = zusatztage
self.ticketnr = tonumber(zusatztage)
if zusatztage == 1 then
if tonumber(is_tagesticket) == 1 then
zusatztage = ""
else
zusatztage = "+ 1 " .. t("Tag")
end
self.ticketanzahl = "1 " .. "x"
elseif zusatztage > 1 then
if tonumber(is_tagesticket) == 1 then
zusatztage = ""
else
zusatztage = "+ " .. tostring(zusatztage) .. " " .. t("Tage")
end
self.ticketanzahl = tostring(self.ticketanzahl) .. " " .. "x"
else
zusatztage = ""
self.ticketanzahl = "0 " .. "x"
self.ticketanzahl = "V V"
self.ticketanzahl = "" -- doch keine 0er Tickets!
end
if PRUEFCODE % 10 == 1 then
self.anzeige_parkdauer:write(self.ticketanzahl)
self.anzeige_zusatztag:write("")
else
self.anzeige_parkdauer:write(self.parkzeitende_hm)
self.anzeige_zusatztag:write(zusatztage)
end
if self.hour == -1 then
self.hour = math.floor((parkdauer_in_sec+30)/3600)
self.min = math.floor(((parkdauer_in_sec+30) - self.hour*3600)/60)
end
-- if file_open == nil then
-- self.parkzeit:write( string.format('Parking Time: %2d:%02d',hour,min) )
-- self.header_message:write( self:date(t('DATUM'))..'\r\n'..self.park_bis_hm )
-- end
if nr > 1 then
self.qr_ordernr = nil
end
self.show_text_muenzen0 = self.show_text_muenzen
if self.amount_in_euro >= tonumber(self.min_sum[self.tarifnr]:read()) then
self.text1:set_visiable(1)
self.text2:set_visiable(1)
self.text3:set_visiable(0)
self.text4:set_visiable(0)
self.show_text_muenzen = 0
else
self.text1:set_visiable(0)
self.text2:set_visiable(0)
self.text3:set_visiable(1)
self.text4:set_visiable(1)
self.show_text_muenzen = 1
end
if self.coin:get_text(278) == 278 then -- Zusatzcode, falls alte coin.tft ohne text3 und text4
self.text1:set_visiable(1)
self.text2:set_visiable(1)
if self.amount_in_euro >= tonumber(self.min_sum[self.tarifnr]:read()) then
self.text1:write("KNOPFDRUECKENA")
self.text2:write("KNOPFDRUECKENB")
self.show_text_muenzen = 0
else
self.text1:write("BITTEMUENZENA")
self.text2:write("BITTEMUENZENB")
self.show_text_muenzen = 1
end
end
self.coin:activate(1)
-- print(nr,self.amount_in_euro,self.min_sum[1]:read())
if self.hoechstparkdauer_ist_erreicht == 1 and (self.real_coins == 1 or self.amount_in_euro < 0.000001) or
nr == 1 and self.amount_in_euro >= tonumber(self.min_sum[self.tarifnr]:read()) then
self.last_knob_action_was_press = 0
self.falsche_qr_eingabe = {}
if self.real_coins == 0 and self.amount_in_euro > 0.00 then
self.no_timeout = 1
self:renew_qr_timeout()
math.randomseed(tonumber(self:time())*1000+tonumber(get_timer_value(29)))
if self.qr_ordernr == nil then
self.qr_ordernr = math.random(10002,99987) -- fuenfstellige zufaellige QR Ordernummer, d.h. die mit 0 beginnenden Codes
end -- koennen als Testnummern verwendet werden
self:compute_html_parameter()
local html_link = "https://pay.12park.de/pay?order="..self.transmit_nr
print(html_link)
self.qrcode_text:write(html_link)
self.qrzaehler = self.qrzaehler + 1
self.qrtan:write("")
self.nochmal:set_visiable(1)
self.nochmal_button:set_visiable(1)
self:aktiviere_qr_tastenfeld(-1)
self.qrcode:activate()
else
stop_timer(7)
if self.show_text_muenzen ~= self.show_text_muenzen0 then
self.text1:set_visiable(0)
self.text2:set_visiable(0)
self.text3:set_visiable(0)
self.text4:set_visiable(0)
end
-- self.uart_sperre = 1
start_timer(7,7,0,1)
end
end
end
--**********************************************************************
function App.decode_order (self,order)
end
--**********************************************************************
function App.generate_tarif_3 (self)
return(1)
end
--**********************************************************************
function App.reset_coin (self)
self.anzeige_drucken:set_visiable(1)
self.who_logged_in_as_admin = ''
self.last_entered_pin = ''
self.anzeige_betrag:write('0,00')
local fff = self:date(t('ZEIT'))
self.anzeige_parkdauer:write(fff)
self.sondertarif_anzeige:set_visiable(1)
self.sondertarif_text = ''
self.qrzaehler = 0
self.tarifnr = 1
self.ticket_wird_gerade_gedruckt = 0
self.real_coins = 0
self.hoechstparkdauer_ist_erreicht = 0
self.no_more_coins = 0
self.nochmal:set_visiable(-1)
self.nochmal_button:set_visiable(-1)
self.show_text_muenzen = 0
self.text1:set_visiable(1)
self.text2:set_visiable(1)
self.aktuelle_pin_eingabe = ''
if self.text3 ~= nil then
self.text3:set_visiable(0)
self.text4:set_visiable(0)
end
if tonumber(self.pay_mode[1]:read()) > 0 then
self.muenzen:set_visiable(1)
self.muenzen1:set_visiable(1)
self.muenzen2:set_visiable(1)
self.muenzen3:set_visiable(1)
self.bargeldlos:set_visiable(1)
else
self.muenzen:set_visiable(-1)
self.muenzen1:set_visiable(-1)
self.muenzen2:set_visiable(-1)
self.muenzen3:set_visiable(-1)
self.bargeldlos:set_visiable(-1)
end
end
--**********************************************************************
function App.make_tarif_table (self) -- Computes both tarif tables
self.tarif_table = { { {0,0} }, { {0,0} }, { {0,0} } }
self.broetchen = 0
self.wohnmobil = 0
-- print("MAKE TARIF TABLE")
for j = 1,2,1 do
for i = 1,10,1 do
local stunde = self.tarif_par[2*j-1][i]:read()
local preis = self.tarif_par[2*j ][i]:read()
if j == 1 and tonumber(preis) == 0 then
self.broetchen = 1
end
if j == 2 and tonumber(preis) == 0 then
self.wohnmobil = 1
end
if i == 1 and trim(stunde) == "" then
self.tarif_table[j] = nil
break
elseif trim(stunde) ~= "" and trim(preis) ~= "" then
table.insert(self.tarif_table[j], {trim(stunde),trim(preis)} )
end
end
local pph = self.price_per_hour[j]:read()
local pph1 = string.gsub(pph,"[()]","")
if not (self.tarif_table[j] == nil) then
pph1 = "(" .. pph1 .. ")"
end
if not (pph1 == pph) then
self.price_per_hour[j]:write(pph1)
-- self.min_sum[j]:write(msm1)
end
local msm = self.min_sum[j]:read() -- nur fuer den Fall, dass irgendwo unerlaubterweise noch Klammern um den Mindestbetrag stehen
local msm1 = string.gsub(msm,"[()]","")
if not (msm1 == msm) then
self.min_sum[j]:write(msm1)
end
if TEST_SCENARIO == 33 then
if j == 1 then
self.tarif_table[j] = { {0,2}, {24,-2}, {24,-4}, {48,-4}, {48,-6}, {72,-6}, {72,-8}, {96,-8}, {96,-10}, {120,-10} }
end
end
if not (self.tarif_table[j] == nil) then -- erweitere die Tariftabelle fuer den Fall, dass Mehrfachtickets eingfestellt sind
print("TARIF TABLE")
if ( ( not (self.tarif_table[j][10] == nil) ) and
(self.tarif_table[j][8][1] == self.tarif_table[j][9][1]) and
(self.tarif_table[j][8][2] < 0) and
(self.tarif_table[j][9][2] < 0) and
(self.tarif_table[j][10][2] < 0) and
(self.tarif_table[j][8][1] % 24 == 0) and
(self.tarif_table[j][9][1] % 24 == 0) and
(self.tarif_table[j][10][1] % 24 == 0) and
(self.tarif_table[j][10][1] - self.tarif_table[j][9][1] == 24) and
(0 == 0) ) then
local stunde = self.tarif_table[j][10][1]
local act_price = self.tarif_table[j][10][2]
local single_ticket_price = self.tarif_table[j][8][2] - self.tarif_table[j][9][2]
while 0 == 0 do
act_price = act_price - single_ticket_price
table.insert(self.tarif_table[j],{stunde,act_price})
if stunde > 1000 then
break
end
stunde = stunde + 24
table.insert(self.tarif_table[j],{stunde,act_price})
end
end
-- for f,g in pairs(self.tarif_table[j]) do print(f,g[1],g[2]) end
end
end
self.hoechstbetrag = { 0, 0 }
for j = 1,2,1 do
local hoechstparkdauer = math.abs( tonumber(self.max_park_hour[j]:read()) * 3600
+ tonumber(self.max_park_minute[j]:read()) * 60 )
-- local hoechstparkdauer = 3
for i = 2,10,1 do
local stunde0 = self.tarif_par[2*j-1][i-1]:read()
local preis0 = self.tarif_par[2*j ][i-1]:read()
local stunde1 = self.tarif_par[2*j-1][i]:read()
local preis1 = self.tarif_par[2*j ][i]:read()
-- print("A",stunde0)
stunde0 = fcheck(0,tonumber,stunde0)
stunde1 = fcheck(0,tonumber,stunde1)
if (stunde0 == nil) or (stunde1 == nil) then
break
end
stunde0 = math.floor(3600 * stunde0 + 0.5)
stunde1 = math.floor(3600 * stunde1 + 0.5)
if stunde0 <= hoechstparkdauer and hoechstparkdauer <= stunde1 and stunde0 < stunde1 then
print(stunde0,stunde1)
self.hoechstbetrag[j] = 0.01 * math.floor(100 * (preis0 + (preis1 - preis0) / (stunde1 - stunde0) * hoechstparkdauer) + 0.5)
break
end
end
-- print("Hoechstbetrag",j,self.hoechstbetrag[j])
end
-- Berechnung des Hoechstbetrages durch Intervallteilung
-- for j = 1,2,1 do -- obsolet
-- self.tarifnr = j
-- print("HP",hoechstparkdauer)
--
-- local xb = 200
-- local xa = 0
-- local xc = nil
-- local p1 = nil
--
-- while (xb - xa) > 0.0001 do
-- feed_the_dog()
-- self.amount_in_euro = 0.5*(xb - xa) + xa
-- p1 = self:compute_parkdauer()
-- p1 = 0.1 * math.floor(10 * p1 + 0.5)
-- print(xa,self.amount_in_euro,xb,p1,hoechstparkdauer)
-- if p1 < hoechstparkdauer then
-- xa = self.amount_in_euro
-- else
-- xb = self.amount_in_euro
-- end
-- end
--
-- self.hoechstbetrag[j] = 0.01 * math.floor(100*self.amount_in_euro + 0.5)
--
-- print("HH",self.hoechstbetrag[j])
--
-- end
end
--**********************************************************************
function App.compute_parkdauer (self)
self.korrektur_hoechstparkdauer = 0
if self.tarif_table[self.tarifnr] == nil then
local pph = fcheck(0,tonumber,self:get_price_per_hour(self.tarifnr))
-- print("PRICE PER HOUR",pph)
if pph == nil then
pph = 0
end
pph = math.max(0,pph)
local parkdauer_in_sec = 86400
if pph > 0.000001 then
parkdauer_in_sec = 3600 * self.amount_in_euro / pph
end
print("PARKDAUER",parkdauer_in_sec)
return parkdauer_in_sec
else
-- for f,g in pairs(self.tarif_table[self.tarifnr]) do print(f,g[1],g[2]) end
local xa = 0
local ya = 0
local xb = 0
local yb = 99999999
self.ttd = nil
self.tth = nil
for i,entry in ipairs(self.tarif_table[self.tarifnr]) do
local stunde = tonumber(entry[1])
local preis = tonumber(entry[2])
if preis < 0 then
preis = -preis
if stunde < 0 then
stunde = -stunde
if (self.tth == nil) then -- cachen wg performance bei langen Tariftabellen
self.tth = (self.akt_time % 3600 ) / 3600
end
stunde = stunde - self.tth -- angefangene Minuten der angebrochenen Stunde abziehen
self.korrektur_hoechstparkdauer = 3600
else
if (self.ttd == nil) then -- cachen wg performance bei langen Tariftabellen
self.ttd = (self.akt_time % 86400 ) / 3600
end
stunde = 0.5 + stunde - self.ttd -- angefangene Stunden und Minuten des angebrochenen Tages abziehen
self.korrektur_hoechstparkdauer = 86400
end
end
if self.amount_in_euro <= tonumber(preis) then
if preis < yb or preis == yb and stunde < xb then
xb = stunde
yb = preis
seta = 1
end
end
if self.amount_in_euro >= tonumber(preis) then
if preis > ya or preis == ya and stunde > xa then
xa = stunde
ya = preis
setb = 1
end
end
if self.amount_in_euro + 0.01 < tonumber(preis) then -- ab hier sind die Werte der Tabelle nicht mehr interessant
break
end
end
-- print(xa,ya,xb,yb)
local diff = math.abs(yb-ya)
local parkdauer_in_h = math.max(xa,xb)
if diff > 0 then
local verhaeltnis = (self.amount_in_euro - ya)/diff
parkdauer_in_h = (xa + verhaeltnis * (xb - xa))
end
return(parkdauer_in_h*3600)
end
end
--**********************************************************************
function App.get_price_per_hour (self,nr)
local pph = self.price_per_hour[self.tarifnr]:read()
pph = fcheck(0,tonumber,pph)
if pph == nil then
pph = 0
end
pph = math.floor ( pph*10 ) / 10
pph = string.format("%3.2f",pph)
return pph
end
--**********************************************************************
function App.get_gutschein_betrag (self,nr,betrag)
local pph1 = self.price_per_hour[self.tarifnr]:read()
local pph = string.gsub(pph1,"[()]","")
pph = fcheck(0,tonumber,pph)
if pph == nil then
pph = 0
end
self.voucher_percentage = (pph * 100) % 10
if self.voucher_percentage > 8.9 then
self.voucher_percentage = 10.0
end
local betrag1 = fcheck(0,tonumber,trim(string.gsub(betrag,",",".")))
if self.ueberzahlung_ausweisen == 1 then
betrag1 = betrag1 - self.hoechstbetrag[self.tarifnr]
end
if betrag1 == nil then
return nil
end
if betrag1 < 0 then
return nil
end
-- self.ticket_betrag = betrag1
betrag1 = 0.01 * math.floor( betrag1 * 10 * self.voucher_percentage + 0.5 )
if betrag1 < 0.005 then
return nil
end
betrag1 = string.format("%3.2f",betrag1)
betrag1 = string.gsub(betrag1,"%.",",")
return betrag1
end
--**********************************************************************
function App.qrcode_to_string (self,qrtable)
local qr_string = ""
local zaehler = 9
local next_byte = #qrtable
for i=1,#qrtable do
for j=1,#qrtable do
if zaehler > 8 then
qr_string = qr_string .. string.char(next_byte)
next_byte = 0
zaehler = 1
end
local qrcode_cell = qrtable[i][j]
if qrcode_cell > 0 then
next_byte = 2*next_byte + 1
end
zaehler = zaehler + 1
end
end
if zaehler < 9 then
for i=zaehler,8 do
next_byte = 2*next_byte
end
qr_string = qr_string .. string.char(next_byte)
end
return(qr_string)
end
--**********************************************************************
function App.sommer_winterzeit (self)
end
--**********************************************************************
function App.print_parkticket (self) -- laeuft nur auf dem coin screen
self.coin:activate()
stop_timer(29)
self.anzeige_drucken:set_visiable(0) -- hier die Anzeige 'Ticket wird gedruckt' einblenden
self.ticket_wird_gerade_gedruckt = 1
self.bargeldlos:set_visiable(-1)
local gutschein_betrag = self:get_gutschein_betrag(self.tarifnr,trim(self.anzeige_betrag:read()))
if gutschein_betrag ~= nil then
start_timer(2,2400,0,1) -- Wartet, bis der Drucker fertig ist
else
start_timer(2,2400,0,1) -- Wartet, bis der Drucker fertig ist
end
print("PUNKTE:",string.format('%7u',self:read_counter()))
if self.mwst > 0.0001 then
self.mwst = (self.mwst * 1000 + 1)/100
if math.floor(self.mwst % 10) == 0 then
self.mwst = trim(string.format('%2u',math.floor(self.mwst/10)))
else
self.mwst = trim(string.format('%4.1f',self.mwst/10))
self.mwst = self.mwst:gsub('%.',',')
end
end
local act_lang = self.country_table[lang_nr] -- aktuelle Sprache, vom Kunden gewaehlt
local p_lang = string.sub( self.lang_selector:read(),1,2 ) -- Sprache des Parkplatzes
p_lang = LANGUAGES[ self:language_number(p_lang) ] -- Ummappen von Flagge zu Sprache
-- print("LANGS",lang_nr,act_lang,p_lang)
-- print("WES",self.hour,self.min)
feed_the_dog()
local x = ''
if PRUEFCODE % 10 == 3 then
x = self:custom_ticket()
else
x = self:print_parkticket_pre()
end
local parkticket_text = x[1]
local voucher_text = x[2]
local sondertarif_text = x[3]
local print_end_data = 1
if #x > 3 then
local print_end_data = x[4]
end
if not (self.sondertarif_text == "") then
sondertarif_text = self.sondertarif_text
end
feed_the_dog()
if not (act_lang == p_lang) then
x = self:print_parkticket_pre(p_lang)
if p_lang == "DE" then
parkticket_text = replace_diacritic_letters(x[1],1) .. " \n" .. parkticket_text
voucher_text = replace_diacritic_letters(x[2],1) .. " \n" .. voucher_text
sondertarif_text = replace_diacritic_letters(x[3],1) .. sondertarif_text
else
parkticket_text = x[1] .. " \n" .. parkticket_text
voucher_text = x[2] .. " \n" .. voucher_text
sondertarif_text = x[3] .. sondertarif_text
end
end
if not (tonumber(self.tarifnr) == 2) then
sondertarif_text = "---DEL---"
end
feed_the_dog()
-- if self.parkzeitende_hm == '23:59' then
-- parkticket = t('ganzer Tag')
-- end
local loc1 = self.location1:read()
local loc2 = self.location2:read()
local loc3 = self.location3:read()
local pin_owner = ""
if (self.anzeige_betrag:read() == " 0,00") and (string.sub(self.last_entered_pin,1,1) == ".") then
pin_owner = string.sub(string.gsub(self.last_entered_pin,"%A",""),1,2)
pin_owner = string.upper(pin_owner)
end
feed_the_dog()
self.lfd_nr = (self.lfd_nr + 1) % 100
local sign_text = parkticket_text .. sondertarif_text .. pin_owner .. string.format("%02u",self.lfd_nr)
-- print("SIGN_TEXT",sign_text)
sign_text = sign_text:gsub(' ','')
sign_text = sign_text:gsub('\n','')
local md5_text = ( md5lib.hash(sign_text) )
error2("parksign01")
self.parkschein_sign = base27_encode( bin.hextos( self:crypt(sec_key1, md5_text) ) )
error2("parksign02")
self.parkschein_sign = self.parkschein_sign .. pin_owner .. string.format("%02u",self.lfd_nr)
sign_text = voucher_text .. pin_owner .. string.format("%02u",self.lfd_nr)
sign_text = sign_text:gsub(' ','')
sign_text = sign_text:gsub('\n','')
local md5_text = ( md5lib.hash(sign_text) )
error2("vouchersign01")
self.voucher_sign = base27_encode( bin.hextos( self:crypt(sec_key1, md5_text) ) )
error2("vouchersign02")
self.voucher_sign = self.parkschein_sign .. pin_owner .. string.format("%02u",self.lfd_nr)
feed_the_dog()
local sondertarif_text1 = ''
if self.tarifnr == 2 then
sondertarif_text = 'QQx1d!QQx11QQx1ba1'..sondertarif_text
sondertarif_text1 = ' \n'
end
-- local parkzeitende_f_freiticket = "" -- scheint obsolet zu sein
local parkticket =
' \n' ..
'QQx1d@\n' ..
'QW\n' ..
'QW\n' ..
'QQx1ba0QQx1d!QQx00\n' ..
parkticket_text ..
' \n'
if PRUEFCODE % 10 == 0 then
parkticket = parkticket ..
'QQx1d!QQx11QQx1ba1'..self.parkzeitende_day .. '\n' ..
'QQx1d!QQx44QQx1ba1'..self.parkzeitende_hm:gsub('^0','') .. '\n'
else
parkticket = parkticket ..
'QQx1d!QQx11QQx1ba1' .. t("Tickets") .. '\n' ..
'QQx1d!QQx44QQx1ba1'..self.ticketanzahl .. '\n'
end
parkticket = parkticket ..
'QQx1d!QQx00\n' ..
loc1 .. '\n' ..
loc2 .. '\n' ..
loc3 .. '\n' ..
' \n' ..
sondertarif_text .. '\n' .. sondertarif_text1 ..
'QQx1d!QQx00' .. self.parkschein_sign .. '\n'
--string.sub(parkschein_sign,33,64),
local ticket_cut =
'QQx1dVQQx01\n' ..
'QW\n' ..
'QW\n' ..
'QQx1d@\n' ..
'PAUSE2002\n'
feed_the_dog()
local voucher = nil
local add_ticket = nil
print("PRUEFCODE",PRUEFCODE)
if PRUEFCODE > 7 then
--local gg = "" -- zu Testzwecken (Muster QR-Code)
--for i,x in ipairs(cqr) do
-- local x1 = ""
-- for j,y in ipairs(x) do
-- if y == 1 then
-- x1 = x1 .. "o"
-- else
-- x1 = x1 .. " "
-- end
-- end
-- print(x1)
-- gg = gg .. x1 .. "\n"
--end
feed_the_dog()
self:voucher_barcode() -- Barcode oder QR-Code
if #self.transmit_nr > 2 then
if ( 1 == 0 ) then -- Barcode, ist obsolet, wir bieten QR-Code an, siehe ein paar Zeilen weiter unten
add_ticket =
'QQx1dwQQx02\n' ..
'QQx1dhQQx60\n' ..
'QQx1dQQx6bEQQx0C' .. self.transmit_nr .. '\n' ..
'QQx1d!QQx00 \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
'QW \n'
else
if self.model_nr > 0 then -- das koennen nur die neuen Geraete
add_ticket = -- QR Code from printer
'QQx1d(k30QQx31QQx41QQx32QQx00\n' ..
'QQx1d(k30QQx31QQx43QQx08\n' .. -- groesse des qr-codes
'QQx1d(k30QQx31QQx45QQx34\n' .. -- fehlerkorrektur
'QQx1d(kQQx09QQx01QQx31QQx50QQx30' .. self.transmit_nr .. '\n' ..
'QQx1d(k30QQx31QQx51QQx30\n' ..
--'QQx1d(kQQx04QQx00QQx31QQx41QQx31QQx00\n' ..
--'QQx1d(kQQx03QQx00QQx31QQx43QQx04\n' ..
--'QQx1d(kQQx03QQx00QQx31QQx45QQx32\n' ..
--'QQx1d(kQQx0FQQx00QQx31QQx50QQx30' .. self.transmit_nr .. '\n' ..
--'QQx1ba1QQx1d(kQQx03QQx00QQx31QQx51QQx30\n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
'QW \n'
else
feed_the_dog()
add_ticket = self:parkticket_qrcode(self.transmit_nr) .. "\n" ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
'QQx1bQQx331\n' ..
'QW \n'
feed_the_dog()
end
end
end
end
if custom_voucher then
voucher = self:custom_voucher()
end
feed_the_dog()
if voucher == nil then
if gutschein_betrag ~= nil then
voucher =
' \n' ..
' \n' ..
'QQx1d@\n' ..
'QW\n' ..
'QW\n' ..
'QQx1ba0QQx1d!QQx00\n' ..
voucher_text ..
' \n' ..
'QQx1d!QQx11QQx1ba1'..self.parkzeitende_day .. '\n' ..
' \n' ..
'QQx1d!QQx11QQx1ba1'..gutschein_betrag:gsub('%.',',') .. " " .. CURRENT .. '\n' ..
'QQx1d!QQx00\n' ..
loc1 .. ' \n' ..
loc2 .. ' \n' ..
loc3 .. ' \n' ..
' \n' ..
'QQx1d!QQx00\n' ..
self.voucher_sign .. '\n' ..
--string.sub(parkschein_sign,33,64),
' \n' ..
' \n' ..
' \n' ..
'QQx1dVQQx01\n' ..
'QW\n' ..
'QW\n' ..
'QQx1d@\n'
end
end
feed_the_dog()
if add_ticket == nil then
add_ticket = ' \n' .. ' \n' .. ' \n' .. ' \n' .. ' \n' .. ' \n'
end
parkticket = parkticket .. add_ticket .. ticket_cut
if voucher ~= nil then
parkticket = parkticket .. voucher
end
local parkticket_table = {}
for zeile in string.gmatch(parkticket,'[^\n]+') do
table.insert(parkticket_table,zeile) -- string.sub(zeile,1,#zeile-1))
end
feed_the_dog()
if self.tarifnr == 2 then -- das ist immer mit PIN-Eingabe
-- print("PPP-------------------------------------PPP",self.aktuelle_pin_eingabe)
-- local u_key = tostring(self.aktuelle_pin_eingabe) .. "," .. tostring(math.ceil(tonumber(self:time())/86400))
-- print("PP_KEY",u_key)
-- if self.used_pins[u_key] == nil then
-- self.used_pins[u_key] = 1
-- else
-- self.used_pins[u_key] = self.used_pins[u_key] + 1
-- end
-- self.aktuelle_pin_eingabe = ''
local group_key = tostring(self.e_gruppen_id)
-- local group_datum = tostring(math.ceil(tonumber(self:time())/86400))
-- if self.datum_aktuelle_ticketliste == group_datum then -- es handelt sich offenbar um einen neuen Tag, daher die Liste der verbrauchten Tickets neu anlegen
-- self.used_pin_groups = {}
-- self.datum_aktuelle_ticketliste = group_datum
-- end
if self.used_pin_groups[group_key] == nil then
self.used_pin_groups[group_key] = {}
end
table.insert(self.used_pin_groups[group_key],self.parkzeitende_tagesticket)
-- self.aktuelle_pin_eingabe = ''
end
feed_the_dog()
if #self.falsche_qr_eingabe > 1 and self.falsche_qr_eingabe[1] == "15970" and self.falsche_qr_eingabe[2] == "15463" then
self.falsche_qr_eingabe = {}
start_timer(29,20000,0,1)
else
self.normal_test1 = "1434"
local reports = self:read_report() -- die Daten in das Ticketreporting hineinschreiben
self:update_counter( -100*math.max(1, math.floor(0.99999+self.amount_in_euro*POINTS_PER_EURO) ) )
local current = "Q"
if self.real_coins > 0 then
current = CURRENTSHORT
end
-- self:write_report(reports,self:date("%y%m%d%H%M%S").."B",self.anzeige_betrag:read())
local anz_betr = self.anzeige_betrag:read() -- wenn der Betrag 0,00, wie bei einem Freiticket, dann die ersten Buchstaben aus der vorher gewaehlten PIN-Eingabe
if anz_betr == " 0,00" then -- statt der Nachkommastellen bringen, um anzuzeigen, von welcher PIN das kam.
local betrag_marker = string.gsub(self.last_entered_pin,"%A","")
if #betrag_marker == 1 then
betrag_marker = betrag_marker .. "X"
end
if #betrag_marker > 1 then
anz_betr = "0," .. string.sub(betrag_marker,1,2)
end
self.last_entered_pin = ""
end
self.normal_test = "1336"
error2("wr055")
self:write_report(reports,self:date("%y%m%d%H%M%S")..current,anz_betr)
error2("wr056")
start_timer(29,3300000,0,1)
end
WAIT_26 = 400 -- Intervall, in dem auf den naechsten Block der Signatur-Berechnung
self.squeeze_out = nil -- gewartet wird. Nur volle Ticketreports werden gedruckt.
start_timer(25,10000,0,1) -- Starten der Berechnung. Diese wird abgebrochen, wenn irgendeine Aktion am Automaten erfolgt.
-- Das ist aber nicht schlimm, beim naechsten Ticketdruck wird ja nochmal angefangen. Bis es durchgegangen ist.
local sum = tonumber( self.anzahl_gedruckte_parktickets:read() )
self.anzahl_gedruckte_parktickets:write( string.format("%1u",sum+1) )
self.sondertarif_text = '' -- wahrscheinlich nicht notwendig hier, wird ja in reset_coin zurueckgesetzt
self:send_to_printer(parkticket_table)
feed_the_dog()
-- Parkticket verschluesselt abspeichern
local reportfile = self.parkschein_sign -- .. '_' .. 'parkticket_' .. SERIALNUMBER .. '_' .. self:date('%y%m%d') .. "_" .. self:date('%H%M%S') .. ".txt"
self.jahresordner = self:date('%y')
self:store_encrypted_file(string.gsub(parkticket,"---DEL---\n",""),md5lib.hash(reportfile))
if (self.NO_CHECK_FOR_SIGNATURE == 0) and (self.flash_is_not_signed == 0) and (self.report_is_integer == 1) then -- fuer Debugging des Signatur-Fehlers
self.normal_test = "1337"
error2("wr60")
self:write_report(reports,self:date("%y%m%d%H%M%S")..current,anz_betr)
error2("wr61")
if (1 - self:check_flash_signature()) == 1 then
self.normal_test = "1338"
error2("wr62")
self:write_report(reports,self:date("%y%m%d%H%M%S").."X","0,06")
error2("wr63")
self.NO_CHECK_FOR_SIGNATURE = 1
end
if self.report_is_integer0 ~= self.report_is_integer then
self.normal_test = "1339"
error2("wr64")
self:write_report(reports,self:date("%y%m%d%H%M%S").."X","0,05")
error2("wr65")
self.NO_CHECK_FOR_SIGNATURE = 1
end
end
feed_the_dog()
-- Verschluesselung Ende
if file_open == nil then
-- for i,zeile in ipairs(parkticket) do
-- print(zeile)
-- end
print("RESTLICHE PUNKTE (x 100):",self:read_counter())
end
end
--**********************************************************************
function App.parkticket_qrcode (self,text)
CALCULATE_QRCODE_FOR_REPORT = 0
local dd, cqr = qrc.qrcode(text)
CALCULATE_QRCODE_FOR_REPORT = 0
feed_the_dog()
local erg, dotres = self:qr_to_bitmap(6,cqr)
feed_the_dog()
local padding = 'QQx1b$QQx70QQx00' -- .. string.format("%02x",(384 - #cqr*6)/2)
local text1 = 'QQx1bQQx330QQx1bQQx61QQx48\n'
-- padding = ""
for i=1,#erg do
feed_the_dog()
local laenge = #erg[i]/lensym
local nr1 = string.format("%02x",laenge % 256)
local nr2 = string.format("%02x",math.floor(laenge/256))
text1 = text1 .. padding .. 'QQx1b*QQx' .. dotres .. "QQx" .. nr1 .. "QQx" .. nr2 .. erg[i] .. "\n"
end
return(text1)
end
--**********************************************************************
function App.print_parkticket_pre (self,langcode)
local tn = nil
print("TN0",langcode,tn)
if not (langcode == nil) then
tn = self:language_number(langcode)
end
print("TN",langcode,tn)
local parkticket_dauer = string.format("%1u",self.hour)..' '..t('Std.',tn)..' '..string.format("%1u",self.min)..' '..t('Min.',tn)
-- if self.parkzeitende_hm == '23:59' then
-- parkticket = t('ganzer Tag')
-- end
local ust = '---DEL---'
if self.umsatzsteuer > 0.001 then
ust = self:ext_line( t('davon',tn) .. ' ' .. self.mwst .. '% ' .. t('Umsatzsteuer',tn)..':xXxXxXxXx'..trim(string.format('%5.2f',self.umsatzsteuer):gsub('%.',','))..' '..CURRENT )
end
local bargeld = t('Barzahlung',tn)
if self.real_coins == 0 then
bargeld = t('bargeldlos',tn)
end
local datum1 = self:date(t('DATUM',tn),self.akt_time)
local ticketvom = self:ext_line( t('Ticket vom',tn)..'xXxXxXxXx'..datum1..' '..self:date(t('ZEIT',tn),self.akt_time):gsub('^0','') )
local parkscheinvom = self:ext_line( t('Parkschein vom',tn)..'xXxXxXxXx'..datum1..' '..self:date(t('ZEIT',tn),self.akt_time):gsub('^0','') )
local parkpreis = trim(self.anzeige_betrag:read())
-- print("PP0",parkpreis)
if self.ueberzahlung_ausweisen == 1 then
local parkpreis1 = string.gsub(parkpreis,',','.')
parkpreis1 = fcheck(0,tonumber,parkpreis1)
-- print("PP",parkpreis1)
if (parkpreis1 ~= nil) and (tonumber(parkpreis1) > 0) then
parkpreis = 0.01 * math.floor(100*(tonumber(parkpreis1) - self.hoechstbetrag[self.tarifnr])+0.5)
self.umsatzsteuer = parkpreis * self.mwst / (100 + self.mwst)
parkpreis = string.format("%3.2f",parkpreis)
parkpreis = string.gsub(parkpreis,'%.',',')
end
if self.umsatzsteuer > 0.001 then
ust = self:ext_line( t('davon',tn) .. ' ' .. self.mwst .. '% ' .. t('Umsatzsteuer',tn)..':xXxXxXxXx'..trim(string.format('%5.2f',self.umsatzsteuer):gsub('%.',','))..' '..CURRENT )
end
end
parkpreis = self:ext_line( t('Preis',tn)..': '..parkpreis..' '..CURRENT..'xXxXxXxXx'..bargeld )
local gutscheinvom = self:ext_line( t('Gutschein vom',tn)..' '..datum1..'xXxXxXxXx'..self:date(t('ZEIT',tn),self.akt_time):gsub('^0','') )
local gueltig_bis = t('Gueltig bis',tn)..': '
if PRUEFCODE % 10 == 0 then
local park_dauer = self:ext_line( t('Parkdauer',tn)..':'..'xXxXxXxXx'..parkticket_dauer )
local parken_bis = t('Parken bis',tn)..': '
local parkticket =
parkscheinvom .. '\n' ..
parkpreis .. '\n' ..
ust .. '\n' ..
park_dauer .. '\n' ..
parken_bis .. '\n'
local voucher =
gutscheinvom .. '\n' ..
gueltig_bis .. '\n'
local sondertarif = t("Sondertarif",tn)
return({parkticket,voucher,sondertarif})
end
if PRUEFCODE % 10 == 1 then
local parkticket =
ticketvom .. '\n' ..
parkpreis .. '\n' ..
ust .. '\n'
local voucher =
gutscheinvom .. '\n' ..
gueltig_bis .. '\n'
local sondertarif = t("Sondertarif",tn)
return({parkticket,voucher,sondertarif})
end
if PRUEFCODE % 10 == 2 then
return({"TICKET",'',''})
end
end
--*********************************************************************
function App.compute_html_parameter (self)
self:voucher_barcode(1)
end
--*********************************************************************
function App.voucher_barcode (self,is_qrcode)
-- print("PARS",self.akt_time,self.ticketnr,self.amount_in_euro)
local gmt_time = self.akt_time - 360*self.shift_time
if self.zeitumstellung_wert < 0 then
gmt_time = gmt_time + self.zeitumstellung_wert -- die naechste Zeitumstellung ist zu Winterzeit, es ist also Sommerzeit, daher diesen
end -- zusaetzlichen negativen Wert hier abziehen
local modulo_time = gmt_time % (32768*32768/8)
local modulo_price = math.floor(self.amount_in_euro*100+0.001) % 32768
local modulo_anzahl = self.ticketnr % 8
local time_anzahl = modulo_anzahl * (32768*32768/8) + modulo_time
local time_price = time_anzahl * 32768 + modulo_price -- wg fuehrender Nullen
print(time_price,self.ticketnr,modulo_anzahl,time_anzahl)
self.transmit_nr = ""
local drei = 1
local anz_pruefcodes = math.floor(#VOUCHER_SIGNA_TABLE/drei) - VOUCHER_M
local lookup_line = time_price % anz_pruefcodes
print (lookup_line," LOOKUP_LINE ---------------")
local serno_ordernr = ''
if (is_qrcode == 1) then
if (tonumber(self.akt_time) > tonumber(QRCODE_VALIDITY_SEC)) then
return
end
local modulo_serno = SERIALNUMBER % 32768
serno_ordernr = modulo_serno * 32768 + (self.qr_ordernr % 32768)
lookup_line = (lookup_line + serno_ordernr % anz_pruefcodes) % anz_pruefcodes
serno_ordernr = string.format("%1x",serno_ordernr)
else
if (tonumber(self.akt_time) > tonumber(PRUEF_VALIDITY_SEC)) then
return
end
end
local voucher_signature = string.sub(VOUCHER_SIGNA_TABLE,drei*lookup_line+1,drei*lookup_line+3)
local transmit_nr_hex = string.format("%1x",32768*32768*32768+time_price)..serno_ordernr
-- print(time_price,serno_ordernr)
local transmit_nr_s = bin.hextos( transmit_nr_hex )
local transmit_nr = base32.encode( transmit_nr_s )
self.transmit_nr = string.sub(transmit_nr,2) .. voucher_signature
-- print(self.transmit_nr)
end
--******************************************************************
function App.xxvoucher_barcode (self,is_qrcode)
self.akt_time = 1723651252
self.amount_in_euro = 2
local modulo_time = self.akt_time % (326768*32768*4)
local modulo_price = math.floor(self.amount_in_euro*100+0.01) % 32768
print(modulo_time,modulo_price)
modulo_anzahl = self.ticketnr % 8
local time_anzahl = 32768*32768 + modulo_anzahl * (32768*32768/8) + modulo_time
local time_price = time_anzahl * 32768 + modulo_price
print("ALT",time_price,self.ticketnr,modulo_anzahl,time_anzahl)
self.transmit_nr = ""
local anz_pruefcodes = math.floor(#VOUCHER_SIGNA_TABLE/3) - VOUCHER_M
local lookup_line = time_price % anz_pruefcodes
local serno_ordernr = ''
if (is_qrcode == 1) then
if (self.akt_time > QRCODE_VALIDITY_SEC) then
return
end
local modulo_serno = SERIALNUMBER % 32768
serno_ordernr = modulo_serno * 32768 + (self.qr_ordernr % 32768)
lookup_line = (lookup_line + serno_ordernr % anz_pruefcodes) % anz_pruefcodes
serno_ordernr = string.format("%1x",serno_ordernr)
else
print("CHECK",self.akt_time,PRUEF_VALIDITY_SEC)
if (self.akt_time > PRUEF_VALIDITY_SEC) then
return
end
end
local voucher_signature = string.sub(VOUCHER_SIGNA_TABLE,3*lookup_line+1,3*lookup_line+3)
local transmit_nr_hex = string.format("%1x",time_price)..serno_ordernr
local transmit_nr_s = bin.hextos( transmit_nr_hex )
local transmit_nr = base32.encode( transmit_nr_s )
print("TRANSMIT",transmit_nr,lookup_line,voucher_signature)
self.transmit_nr = string.sub(transmit_nr,2) .. voucher_signature
end
--************************************************************************
function App.voucher_barcode_reverse (self)
local a = string.sub(self.transmit_nr,1,1)
local b = string.sub(self.transmit_nr,2,6)
local c = string.sub(self.transmit_nr,7,9)
local d = string.sub(self.transmit_nr,10,12)
b_time1 = self.akt_time % (32768*32*32)
b_time2 = base32.decode( b )
b_time2 = bin.stohex( b_time2 )
b_time2 = tonumber("0x" .. b_time2)
print(b,b_time1,b_time9,b_time2)
print( base32.encode ( bin.hextos( string.format("%1x",self.ticketnr) ) ) )
end
--************************************************************************
function App.custom_voucher (self)
local voucher = nil
return voucher
end
--**********************************************************************
function App.ext_line (self,zeile) -- make_two_lines_out_of_one_if_too_long
local zeile1 = replace_diacritic_letters(zeile)
if #zeile1 > PAPIERBREITE + 8 then
zeile = string.gsub(zeile,"xXxXxXxXx","\n")
for z1 in string.gmatch(zeile,'[^\n]+') do
local z2 = replace_diacritic_letters(z1)
if #z2 > PAPIERBREITE then
zeile = string.gsub(zeile,"\n"," ")
local new_zeile1 = ""
local new_zeile2 = ""
local new_zeile_tr = ""
for z3 in string.gmatch(zeile,'[^ ]+') do
print(z3)
local z3_red = replace_diacritic_letters(z3)
if new_zeile2 == "" then
if #new_zeile_tr + #z3_red + 1 > PAPIERBREITE then
new_zeile2 = new_zeile2 .. z3
elseif new_zeile1 == "" then
new_zeile1 = new_zeile1 .. z3
new_zeile_tr = new_zeile_tr .. z3_red
else
new_zeile1 = new_zeile1 .. " " .. z3
new_zeile_tr = new_zeile_tr .. " " .. z3_red
end
else
new_zeile2 = new_zeile2 .. " " .. z3
end
end
zeile = new_zeile1 .. "\n" .. new_zeile2
-- print("223344 zeile",zeile)
end
break
end
else
zeile = string.gsub(zeile,"xXxXxXxXx"," ")
end
return(zeile)
end
--**********************************************************************
function App.store_encrypted_file (self,filetxt,reportfile,reportfile1)
if reportfile1 ~= nil then
my_write_filedata1(reportfile1,sd_dir..'/'..reportfile1,filetxt,over_write)
end
error2("filesign01")
filetxt = self:crypt(sec_key3,filetxt,80)
error2("filesign02")
reportfile = string.gsub(self:crypt(sec_key3,reportfile,9999),"[\n ]","") .. ".enc"
error2("filesign03")
-- print("WWWWW 223344",self.sign_idx)
if #XFILES > 0 then
reportfile = XFILES[tonumber(self.sign_idx)%#XFILES+1] .. "/" .. reportfile
else
for i,jahr in ipairs(sd_subdirs) do -- herausfinden, ob es ein Jahres-Verzeichnis gibt
if jahr == self.jahresordner then
reportfile = jahr .. '/' .. reportfile
end
end
end
error2("filesign04 "..reportfile)
my_write_filedata1(reportfile,sd_dir..'/'..reportfile,filetxt,over_write)
error2("filesign05")
return(reportfile)
end
--**********************************************************************
function create_signa_table (anzahl,linesize) -- for signature lookup table
local rseed = string.sub(sec_key,4,10) -- choose specific randomseed, depending on serialnumber
-- print("RSEED",rseed)
rseed = bin.hextos(rseed)
rseed = base10.encode(rseed)
-- print("RSEED1",rseed)
math.randomseed(tonumber(rseed))
local max_anzahl = math.floor(tonumber(anzahl) / 3) * 3
local zaehler = 0
-- local ctable = "0123456789ABCDEFGHJKLMNPQRSTWXYZ"
local ctable = "RY1T9W2XF0GZH4JKSA67B3CDEL5M8QPN"
local text = ""
while (zaehler < max_anzahl) do -- hier wird eine neue Pruefcodetabelle geschrieben
rr = math.random(1,32)
rr1 = string.sub(ctable,rr,rr)
zaehler = zaehler + 1
text = text .. rr1
if zaehler % linesize == 0 then
text = text .. "\n"
end
end
-- print(text)
local text1 = string.gsub(text, '%s+', '')
-- print (#VOUCHER_SIGNA_TABLE)
-- print (#text1)
if not (string.gsub(text, '%s+', '') == VOUCHER_SIGNA_TABLE) then -- wenn die neu erzeugte Pruefcodetabelle nicht mit der internen uebereinstimmt
text = string.sub(text,1,#text-30) .. " " .. string.sub(text,#text-29,#text) -- dann wird in der letzten Zeile eine Luecke mit ausgegeben
end
-- print(text1)
return(text)
end
--**********************************************************************
function fcheck (error_val,func,arg1,arg2,arg3,arg4)
function fcheck1 (func,arg1,arg2,arg3,arg4) fcheck_erg = func(arg1,arg2,arg3,arg4) end
if pcall(fcheck1,func,arg1,arg2,arg3,arg4) then
if fcheck_erg == nil then
return error_val
else
return fcheck_erg
end
else
return error_val
end
end
--**********************************************************************
function App.update_counter (self,betrag)
local sum = 0
if betrag > 0 then -- Beim Aufladen der Einzel-Counter denjenigen suchen, der am kleinsten ist
local count0 = nil
local gesamt = 0
for i,count in ipairs(self.counter) do
count_amount = tonumber(count:read())
gesamt = gesamt + count_amount -- ermitteln, ob der Gesamtzaehler vielleicht negativ war
if count0 == nil or count_amount < tonumber(count0:read()) then
count0 = count
end
end
sum = tonumber( count0:read() )
if gesamt < 0 then
betrag = betrag - gesamt -- negativen Gesamtsaldo ausgleichen
end
count0:write( string.format("%1.0f",sum+betrag) ) -- Teilzaehler erhoehen
elseif betrag < 0 then -- beim Abziehen von Punkten einen zufaelligen Teil-Counter waehlen
self.aktueller_teilcounter = (self.aktueller_teilcounter + 1) % #self.counter
sum = tonumber( self.counter[ self.aktueller_teilcounter+1 ]:read() )
self.counter[ self.aktueller_teilcounter+1 ]:write( string.format("%1.0f",sum+betrag) )
end
sum = self:read_counter()
self.counter1:write(string.format('%7u',math.floor(sum/100+0.5)))
end
--**********************************************************************
function App.read_counter (self,raw)
local sum = 0
for i,count in ipairs(self.counter) do
-- if i < 200 then
-- tonumber(count:read())
-- end
sum = sum + tonumber(count:read())
end
return math.max(0,sum)
end
--**********************************************************************
function App.str_to_byteArray(self,str)
local i=1
local k=0
local bytes={}
while i<=#str do
if string.sub(str,i,i+2)=='QQx' or string.sub(str,i,i+2)=='WxxxxxxxxxxxWx' then
bytes[k]=tonumber('0x'..string.sub(str,i+3,i+4))
i=i+5
elseif string.sub(str,i,i+1)=='QW' then
bytes={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
bytes[0]=0 --'QW' erzeugt table mit 64 Nullbytes, start bei index 0, zwei Zeilen mit 'QW' zum Leeren des Buffers
break
else
bytes[k]=string.byte(str,i)
i=i+1
end
k=k+1
end
return bytes
end
--**********************************************************************
function App.read_report (self,rp)
local reports = {}
local text = 'x'
local signature = ''
local checknr = ''
local text1 = ''
local reconstruct = rp
self.report_is_integer0 = self.report_is_integer
local reportnr = fcheck(0,tonumber,self.reportnumber:read())
if reportnr == nil then
reportnr = 0
end
local akt_count = self:read_counter()
-- change_screen(19)
-- if true then return end
local text = self:decrypt( pub_key3, my_read_filedata1('newreport.txt',sd_dir..'/newreport.txt') )
if file_open == nil then
if self.alt_report ~= nil then
print("ALT",self.alt_report)
text = self.alt_report
end
end
local text_normal = normal_signs(text)
if text_normal ~= '' then
error1("XA "..self.normal_test1.. " "..string.sub(text,1,100))
text = ''
end
-- print("223344",text)
for zeile in string.gmatch(text,'[^\n]+') do
if trim(zeile) ~= '' then
if signature == '' then
signature = zeile
elseif checknr == '' then
checknr = zeile
text1 = zeile
else
table.insert(reports,zeile)
text1 = text1 .. zeile
end
end
end
local md5key1 = '---'
if #signature > 5 then
md5key1 = self:decrypt(pub_key1, signature )
end
local md5key = md5lib.hash(text1)
if md5key == md5key1 then
self.report_is_integer = 1
else
self.report_is_integer = 0
end
if file_open == nil then
-- for i,z in ipairs(reports) do print(z) end
return(reports)
end
local checknr0 = SERIALNUMBER .. '/' .. string.format('%1u',akt_count) .. '/' .. string.format('%1u',reportnr)
if true and (checknr0 ~= trim(checknr)) then -- Counter und Counter-Eintrag in der newreport passen nicht zusammen
local akt_zeit_hm_tmp = self:date('%H%M',self:time())
my_write_filedata1('signaerror.txt',sd_dir..'/signaerror.txt',checknr0.."\n"..checknr.."\n"..akt_zeit_hm_tmp.."\n\n",add_write)
local pos1
local pos2 -- vielleicht nur, weil beim letzten Ticketdruck der Strom abgeschaltet war
pos1,pos2 = string.find(checknr,"/.-/")
if (pos1 == nil) or (pos2 == nil) or (not reconstruct == 1) then
-- local pass3 = 33 -- d.h. nichts machen
self.report_is_integer = 0
else
local report_count = tonumber( string.sub(checknr,pos1+1,pos2-1) )
local count_diff = report_count - akt_count
if count_diff > 200 or count_diff < -200 then
self.normal_test = "1340"
error2("wr66")
self:write_report(reports,self:date("%y%m%d%H%M%S").."X","0,07")
error2("wr67")
-- local pass3 = 33 -- d.h. nichts machen
self.report_is_integer = 0
else
if count_diff > 0 then
local lost_amount = count_diff / POINTS_PER_EURO + 1 -- verlorenen Betrag rekonstruieren
lost_amount = math.floor(0.1*lost_amount) * 0.1 + 0.01 -- der eine Cent dient zur Anzeige, dass hier ein Problem war, und der Betrag rekonstruiert worden ist
lost_amount = string.gsub(lost_amount,"%.",",")
local current = CURRENTSHORT
if lost_amount < 0.05 then
current = "Q"
end
-- self:write_report(reports,self:date("%y%m%d%H%M%S").."C",lost_amount)
self.normal_test = "1341"
error2("wr68")
self:write_report(reports,self:date("%y%m%d%H%M%S")..current,lost_amount)
error2("wr69")
-- reports = self:read_report() -- nochmal auslesen, aber nicht mehr zu rekonstruieren versuchen
else
self:update_counter( count_diff )
self.normal_test = "1342"
error2("wr70")
self:write_report(reports,self:date("%y%m%d%H%M%S").."X","0,00")
error2("wr71")
local art_der_signierung = 0
if self:check_flash_signature() == 0 then
self:check_flash_signature('read_report')
art_der_signierung = art_der_signierung + 2
end
if not (self.report_is_integer == 1) then
self.report_is_integer = 0
art_der_signierung = art_der_signierung + 3
end
self.normal_test = "1343"
error2("wr72")
self:write_report(reports,self:date("%y%m%d%H%M%S").."X","0,0"..tostring(art_der_signierung))
error2("wr73")
end
local pin_defs = self.pin_definitions:read()
if (SERIALNUMBER == '0553227') or (SERIALNUMBER == '0123456') or not (string.find(pin_defs,"w56dfgsu1") == nil) then -- bypass battery alert
return(reports)
end
return(nil) -- dies bedeutet, dass man einen Low Batterie Alert anbringt und aussteigt
end
end
end
-- self.report_is_integer = 1 -- fuer Testzwecke @@
return(reports)
end
--**********************************************************************
function App.check_for_battery_alert (self)
local tn = self.lang_nr_for_report
local batt = my_read_filedata1('battery.txt',sd_dir..'/battery.txt')
if batt == nil then
batt = ""
end
if file_open == nil then
return(0)
end
if #batt > 10 then
my_write_filedata1("battery.txt",sd_dir..'/'.."battery.txt"," \n",over_write) -- schon mal die Datei battery.txt ueberschreiben, bevor gedruckt wird!
local batteryinfo = -- denn wenn die Batterie schwach ist, dann stuerzt das Geraet ja beim Drucken des Battery-Alert eh wieder ab
' \n' ..
' \n' ..
'QQx1d@\n' ..
'QW\n' ..
'QW\n' ..
'QQx1ba0QQx1d!QQx00\n' ..
self:ext_line( t('Please check',tn) ).. "\n" ..
self:ext_line( t('battery state',tn) ).. ".\n" ..
' \n' ..
self:ext_line( t('Then restart',tn) ).. ".\n" ..
'QQx1d!QQx00\n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
'QQx1dVQQx01\n' ..
'QW\n' ..
'QW\n' ..
'QQx1d@\n'
local batteryinfo_table = {}
for zeile in string.gmatch(batteryinfo,'[^\n]+') do
table.insert(batteryinfo_table,zeile)
end
self:send_to_printer1(batteryinfo_table) -- nicht die self_to_printer Funktion nehmen, denn Geraet wird ja gleich disabled,
return(1) -- und alle Timer werden ausgeschaltet. self_to_printer funktioniert aber nur mit Timer
else
return(0)
end
end
--**********************************************************************
function App.write_report (self,reports,datum,betrag)
self.last_print_datum = tostring(datum)
if betrag ~= nil then
table.insert(reports,self.last_print_datum..' '..trim(betrag))
end
local reportnr = self.reportnumber_tmp
if reportnr == nil then
reportnr = fcheck(0,tonumber,self.reportnumber:read())
-- print('Q1',reportnr)
end
if reportnr == nil then
reportnr = 0
end
self.reportnumber_tmp = nil
local akt_count = self:read_counter()
local checknr = SERIALNUMBER .. '/' .. string.format('%1u',akt_count) .. '/' .. string.format('%1u',reportnr)
-- print('QC',checknr,reports)
local text1 = checknr
for i,zeile in ipairs(reports) do
text1 = text1 .. zeile
end
local text_normal = normal_signs(text1)
if text_normal ~= '' then
error1("XB "..self.normal_test.." "..string.sub(text1,1,100))
text1 = ''
end
local signature = '---'
if self.report_is_integer == 1 then
local md5key = md5lib.hash(text1)
error2("signsign01")
signature = self:crypt(sec_key1, md5key)
error2("signsign02")
end
-- self.debug1:write("WRITEREPORT_SIGNA"..signature)
text = signature .. '\n' .. checknr .. '\n'
for i,zeile in ipairs(reports) do
-- print("DDD",zeile)
text = text .. zeile .. '\n'
end
-- print(text)
-- print(self:crypt(sec_key3,text,80))
error2("reportsign01")
my_write_filedata1('newreport.txt',sd_dir..'/newreport.txt',self:crypt(sec_key3,text,80),over_write)
error2("reportsign01")
-- print("QWWWW",self.there_is_no_sdcard)
if math.random(1,25) == 1 then
text = my_read_filedata1('newreport.txt',sd_dir..'/newreport.txt')
if text == nil or text == "" then
self.there_is_no_sdcard = 1
end
end
-- print("QWWWW",self.there_is_no_sdcard)
return(1)
end
--**********************************************************************
function App.generate_report (self) local s, r = xpcall(App.p_generate_report,function(e) error1(e) end,self) return(r) end
--**********************************************************************
function App.p_generate_report (self)
-- 1. Report ermitteln
local tn = self:sync_report_data("lang",self.lang_nr_for_report)
local reportnr = self:sync_report_data("reportnr",fcheck(0,tonumber,self.reportnumber:read()))
if reportnr == nil then
reportnr = 0
end
reportnr = reportnr + 1
self.report_is_integer = self:sync_report_data("rinteger",self.lang_nr_for_report)
local short_report
local stxt1
local report_txt
local reportfile
local zaehler = 0
self.mz = ""
error2("---")
error2_shift()
while 0 == 0 do
self:interrupt_report_computation("Setup main loop 1 for report, WAIT26")
error2("mainloop0004")
zaehler = zaehler + 1
local spalten = self:generate_report_spalten_berechnen(tn)
if spalten == nil then
return(0)
end
reportfile = 'ticketreport_' .. SERIALNUMBER .. '_' .. self:date('%y%m%d') .. "_" .. self:date('%H%M%S')
reportfile = reportfile .. '_' .. string.format('%04u',reportnr) .. '.txt'
self.jahresordner = self:date('%y')
self:interrupt_report_computation("Setup main loop 2 for report, WAIT26")
error2("mainloop0005")
report_txt = self:generate_report_write_complete(spalten,reportnr,tn)
if report_txt == nil then
return(0) -- Reportberechnung wurde aus bestimmten Gruenden bei der Spaltenberechnung abgebrochen
end
self:interrupt_report_computation("Setup main loop 3 for report, WAIT26")
error2("mainloop0006")
short_report, stxt1 = self:make_short_report() -- in stxt1 steht der alternative Text zum QR-Code drin
error2("mainloop0007")
-- print(sr32)
if #short_report < MAXTRANSMIT then -- dann ist noch Platz fuer 22 zeichen fuer die URL-Angabe
break
end
local max_zeilen = self.report_data["maxzeilen"] -- Short report ist zu lang, daher die max Zeilenzahl verkuerzen
if max_zeilen < 10 then -- aber nicht unter diese Zeilenzahl
break
end
self.mz = self.mz .. string.format("%02u",tonumber(max_zeilen))
self.report_data["maxzeilen"] = max_zeilen - 8 -- Report um 8 zeilen verkuerzen
self.report_data["signastamp"] = nil
self.report_data["location"] = nil
self.report_data["whoadm"] = nil
self.report_data["reports"] = nil
self.reporthashes = self.reporthashes0 -- falls in make_short_report schon geandert wurde
end
-- print("SR SR",type(short_report),short_report)
-- 2. den short report in kodierter Foerm noch dranhaengen (braucht man eig nicht)
error2("mainloop0008")
short_report1 = short_report
local zeilenlaenge = PAPIERBREITE --
while zeilenlaenge ~= nil do
table.insert(report_txt,string.sub(short_report1,1,zeilenlaenge))
short_report1 = string.sub(short_report1,zeilenlaenge+1)
if #short_report1 == 0 then
break
end
end
-- 2a. nur fuer Testzwecke
table.insert(report_txt," ")
table.insert(report_txt,string.format("%06u",zaehler*100000+#short_report) .. " " .. self.mz)
-- 3. Abspeichern report
error2("mainloop0009")
self.reportnumber:write(string.format("%1u",reportnr))
self.reportnumber_tmp = reportnr
self.normal_test = "1344"
error2("wr74")
self:write_report(self.report_data["reports"])
error2("wr75")
local reportfile = self:write_report_to_file(report_txt,reportnr)
error2("wr76")
-- 4. startsection fuer print of report
local rtxt1 = {
--'QQx1d@', -- printer initialize (oder 1b@??, haben wir jemals den Printer initialisiert??)
--'QW',
--'QW',
'QQx1ba0QQx1d!QQx00', -- 1b (ESC) a0: linksbuendig, 1d (GS) ! 0: Schriftgroesse 0
}
error2("mainloop0010")
-- 5. Laengenangabe machen für shortreport
self.sr = short_report -- fuer test
short_report = "https://12park.de/x/" .. string.sub(short_report,1,300)
local len_short_report = #short_report + 3
local stxt_part_len = math.floor(len_short_report/5)
local len_angabe = "QQx00"
if len_short_report > 255 then
len_angabe = "QQx01"
len_short_report = len_short_report - 256
end
len_short_report = string.format("%02x",len_short_report)
len_angabe = "QQx" .. len_short_report .. len_angabe
local short_report_1 = string.sub(short_report,0*stxt_part_len+1,1*stxt_part_len)
local short_report_2 = string.sub(short_report,1*stxt_part_len+1,2*stxt_part_len)
local short_report_3 = string.sub(short_report,2*stxt_part_len+1,3*stxt_part_len)
local short_report_4 = string.sub(short_report,3*stxt_part_len+1,4*stxt_part_len)
local short_report_5 = string.sub(short_report,4*stxt_part_len+1,5*stxt_part_len+9999)
-- 6. QR-Code Ausgabe f shortreport
table.insert(stxt1,1,' ')
table.insert(stxt1,1,string.format("%03u",reportnr))
table.insert(stxt1,1,' ')
table.insert(stxt1,'QW')
if self.model_nr == 1 then -- QR-Code Ausgabe nur auf neuen Geraeten
table.insert(stxt1,'QQx1ba1') -- mittig drucken
table.insert(stxt1,'QQx1d(k40QQx31QQx41QQx31QQx00') -- QR-Code Style: 31 (31,32)
table.insert(stxt1,'QQx1d(k30QQx31QQx43QQx04') -- QR-Code Pixelvielfachheit: 4
table.insert(stxt1,'QQx1d(k30QQx31QQx45QQx34') -- QR-Code Correction 34 (2f..34)
table.insert(stxt1,'---DEL---6') -- damit werden die naechsten 6 Zeilen nicht mit Zeilenvorschub versehen
table.insert(stxt1,'QQx1d(k'..len_angabe..'QQx31QQx50QQx30')
table.insert(stxt1,short_report_1) -- Speichern QR-Code
table.insert(stxt1,short_report_2)
table.insert(stxt1,short_report_3)
table.insert(stxt1,short_report_4)
table.insert(stxt1,short_report_5)
table.insert(stxt1,string.format("%03u",reportnr))
--table.insert(stxt1,'QQx1d(k'..len_angabe..'QQx31QQx50QQx30' .. short_report .. string.format("%03u",reportnr) ..
table.insert(stxt1,'QQx1d(k30QQx31QQx51QQx30') -- Ausdrucken QR-Code
table.insert(stxt1,'QW')
table.insert(stxt1,'QW')
end
error2("mainloop0011")
table.insert(stxt1,' ')
table.insert(stxt1,' ')
table.insert(stxt1,' ')
table.insert(stxt1,' ')
table.insert(stxt1,' ')
table.insert(stxt1,'QQx1dVQQx01') -- paper cut half
table.insert(stxt1,'PAUSE1311')
table.insert(stxt1,'QW')
table.insert(stxt1,'QW')
table.insert(stxt1,'QQx1d@') -- printer initialize (oder 1b@??, haben wir jemals den Printer initialisiert??)
table.insert(stxt1,'QW')
table.insert(stxt1,'QW')
table.insert(stxt1,' ')
local prbuf1 = {}
local prbuf2 = {}
for i,zeile in ipairs(rtxt1) do
table.insert(prbuf1,zeile)
table.insert(prbuf2,zeile)
end
if PRINT_BF_EXIST == 1 then
for i,zeile in ipairs(report_txt) do
table.insert(prbuf1,string.sub(zeile,1,32))
end
end
for i,zeile in ipairs(stxt1) do
table.insert(prbuf1,zeile)
table.insert(prbuf2,zeile)
end
self:add_to_printbuffer( prbuf1 )
self:add_to_printbuffer2( prbuf2 )
error2("mainloop0012")
return(reportfile)
end
--************************************************************************
function App.add_to_printbuffer (self,text1)
-- if file_open ~= nil then
-- print("WWWW",self.squeeze_out)
if self.squeeze_out == nil then
local printtext = self:decrypt( pub_key3, my_read_filedata1('printbuffer.txt',sd_dir..'/printbuffer.txt') )
if printtext == nil then
printtext = ""
end
for i,zeile in ipairs(text1) do
printtext = printtext .. zeile .. "\n"
end
error2("addsign01")
printtext = self:crypt(sec_key3,printtext,80) -- weil da Klartext drin ist, muss verschluesselt werden
error2("addsign02")
my_write_filedata1('printbuffer.txt', sd_dir..'/printbuffer.txt',printtext,over_write)
else -- der Printbuffer ist leer, weil alle vorraetigen Reports schon ausgedruckt sind. Deswegen muss dieser nicht mehr gelesen werden
print("SQUEEZE_OUT")
self:send_to_printer(text1) -- direkter Druck, anstelle in den Printbuffer zu schreiben
-- self.report_wird_gedruckt:set_visiable(0)
end
-- end
end
--************************************************************************
function App.add_to_printbuffer2 (self,text1)
local printtext2 = my_read_filedata1('printbuffer2.txt',sd_dir..'/printbuffer2.txt')
if printtext2 == nil then
printtext2 = ""
elseif #printtext2 < 16 then
printtext2 = ""
else
printtext2 = self:decrypt( pub_key3, printtext2 )
end
for i,zeile in ipairs(text1) do
printtext2 = printtext2 .. zeile .. "\n"
end
error2("addbuf01")
printtext2 = self:crypt(sec_key3,printtext2,80) -- weil da Klartext drin ist, muss verschluesselt werden
error2("addbuf02")
my_write_filedata1('printbuffer2.txt', sd_dir..'/printbuffer2.txt',printtext2,over_write)
end
--************************************************************************
function App.write_report_to_file (self,report_txt,reportnr)
local filetxt = ''
for i,zeile in ipairs(report_txt) do
filetxt = filetxt..zeile..'\n'
end
-- filetxt = filetxt..' \n'
local klartext_report = nil
if file_open == nil then
klartext_report = "report_" .. SERIALNUMBER .. "_" .. string.format("%03u",reportnr) .. ".txt"
end
local filename = self:store_encrypted_file(filetxt,md5lib.hash(self.signature),klartext_report)
return(filename)
end
--************************************************************************
function App.sync_report_data (self,fname,val)
if self.report_data[fname] ~= nil then
return(self.report_data[fname])
else
self.report_data[fname] = val
return(val)
end
end
--************************************************************************
function App.write_report_header_template (self,tn)
local text1 = {} -- Header
for zeile_text1 in string.gmatch( self:ext_line(t('Protokoll verkaufte Parkscheine',tn)) ,'[^\n]+') do
table.insert(text1,zeile_text1)
end
table.insert(text1,self.location1:read())
table.insert(text1,t('PROTOKOLLNR',tn)..' '..SERIALNUMBER..' / _SERNO_')
table.insert(text1,t('erzeugt',tn)..' _DATUM_ _ZEITS_')
table.insert(text1,'_WHO_')
self:sync_report_data("header",text1)
self:sync_report_data("signatitle",string.sub(t('Signatur',tn).." ",1,10))
self:sync_report_data("footer",self:website())
self:sync_report_data('sformat',t('ZEITS',tn))
if (self.who_logged_in_as_admin ~= nil) and (self.who_logged_in_as_admin ~= '') then
self.report_data['whoadmin'] = self.who_logged_in_as_admin
else
self.report_data['whoadmin'] = ""
end
end
--************************************************************************
function App.write_report_data (self,reportnr,tn)
self:sync_report_data("reportnr", reportnr)
self:sync_report_data("reportdatum", self:time())
self:sync_report_data("who", t('VON',tn))
local who_admin = ""
if (self.who_logged_in_as_admin ~= nil) and (self.who_logged_in_as_admin ~= '') then
local who_admin = self.who_logged_in_as_admin
end
who_admin = self:sync_report_data("whoadmin",who_admin)
local text1 = {}
-- print(self.report_data['header'],reportnr)
for i,zeile in ipairs(self.report_data['header']) do
local zeile1 = zeile
zeile1 = string.gsub(zeile1,"_SERNO_",string.format("%03u",self.report_data['reportnr']))
zeile1 = string.gsub(zeile1,"_DATUM_",self:date(self.report_data['dformat'],self.report_data['reportdatum']))
zeile1 = string.gsub(zeile1,"_ZEITS_",self:date(self.report_data['sformat'],self.report_data['reportdatum']))
zeile1 = string.gsub(zeile1,"_WHO_", self.report_data['who'])
if zeile1 ~= "" then
table.insert(text1,zeile1)
end
end
if who_admin ~= "" then
table.insert(text1,string.sub(who .. ": " .. who_admin .. " ",1,32))
end
return(text1)
end
--************************************************************************
function App.spalten_zweispaltig_formatieren (self,spalten)
local text2 = {}
for i = 1,2,1 do
while #spalten[i] > 0 do
if trim(spalten[i][ #spalten[i] ]) == '' then
table.remove(spalten[i])
else
break
end
end
end
local add_leerzeile = #spalten[1] >= #spalten[2]
if add_leerzeile then
add_leerzeile = not (string.sub(spalten[1][#spalten[1]],1,12) == " ")
end
while #spalten[1] > 0 or #spalten[2] > 0 do -- hier die Spalten zusammenfuehren
zeile = table.remove(spalten[1],1)
if zeile == nil then
zeile = ''
end
zeile = string.sub(zeile .. ' ',1,16)
--print(zeile, table.remove(spalten[2],1))
zeile = zeile .. ' ' .. (table.remove(spalten[2],1) or '')
table.insert(text2,zeile)
end
return(text2)
end
--************************************************************************
function App.write_report_text (self)
local text1 = {}
for i,zeile in ipairs(report_data['header']) do
local zeile1 = string.gsub(zeile,"-SERNO-",report_data['reportnr'])
zeile1 = string.gsub(zeile,"-DATUM-",self:date(report_data['dformat'],report_data['reportdatum']))
zeile1 = string.gsub(zeile,"-ZEITS-",self:date(report_data['sformat'],report_data['reportdatum']))
zeile1 = string.gsub(zeile,"-WHO-", report_data['who'])
if zeile1 ~= "" then
table.insert(text1,zeile1)
end
end
end
--************************************************************************
-- die Spalten sind fertig, jetzt das eigentliche Protokoll schreiben
function App.generate_report_write_complete (self,spalten,reportnr,tn)
local header_template = self:write_report_header_template(tn)
local header = self:write_report_data(reportnr,tn)
local spaltentext = self:spalten_zweispaltig_formatieren(spalten)
self:interrupt_report_computation("Spalten fertig, WAIT26")
error2("signa0001")
local signashort = self:make_signa_report(header,spaltentext,reportnr)
error2("signa0002")
local signastamp = self:sync_report_data("signastamp", signashort )
if (signashort ~= signastamp) then
return(nil)
end
-- 5. jetzt den Text des Reports schreiben
local text1 = header
if self.report_is_integer ~= 1 then
table.insert(text1,' ')
table.insert(text1,t('Nicht signierter Ausgabereport.',tn))
table.insert(text1,t('Nur nachrichtliche Werte.',tn))
table.insert(text1,t('Kann nicht als Umsatznachweis',tn))
table.insert(text1,t('fuer die Steuerbehoerden',tn))
table.insert(text1,t('verwendet werden.',tn))
else
local sr2 = self.report_data['signatitle'] .. self.signature
table.insert(text1,string.sub(sr2,1,32))
table.insert(text1,string.sub(sr2,33,64))
end
table.insert(text1,' ')
for i,zeile in ipairs(spaltentext) do
table.insert(text1,zeile)
end
if string.sub(text1[#text1],1,14) ~= " " then
table.insert(text1,"")
end
table.insert(text1,self:website())
table.insert(text1,"")
self:interrupt_report_computation("Report fertig, WAIT26")
error2("signa0003")
return(text1)
end
--************************************************************************
function App.interrupt_report_computation (self,text)
if file_open ~= nil then
start_timer(26,WAIT_26,0,1)
coroutine.yield()
else
if text ~= nil then
notify(text)
end
end
end
--************************************************************************
function App.generate_report_spalten_berechnen(self,tn)
self.normal_test1 = "1435"
local reports = self:sync_report_data("reports", self:read_report())
-- for i,z in ipairs(reports) do print(z) end
self.report_is_integer = self:sync_report_data("rinteger", self.report_is_integer)
if #reports == 0 then
self:reset_squeeze()
return(nil)
end
error2("generate9001")
local mwst = self:sync_report_data("ust", fcheck(0,tonumber,self.tax_vat:read()))
local max_zeilen = self:sync_report_data("maxzeilen", self:update_pruefcode())
local dformat = self:sync_report_data("dformat", t("DATUM",tn))
local zformat = self:sync_report_data("zformat", t("ZEIT",tn))
self.mz = self.mz .. "(" .. string.format("%02u",tonumber(max_zeilen)) .. ")"
max_zeilen = math.max(max_zeilen-6,10)
local spalten = { {}, {} }
self.nr = 1 -- Spaltennummer, bei self.nr = 3 ist das Ticket fertig
local contains_real_entries = 0
local zeile = ''
self.heartbeat_intervals = {}
error2("generate9002")
table.insert(self.heartbeat_intervals,"")
if self.squeeze_out == nil then -- der Report wird nicht voll, also gleich zurueck
if #reports < max_zeilen - 2 then
self:reset_squeeze()
return(nil)
end
end
error2("generate9003a")
mwst = (mwst * 1000 + 1)/100
if math.floor(mwst % 10) == 0 then
error2("generate9003b")
mwst = string.format('%2u',math.floor(mwst/10))
else
error2("generate9003")
mwst = trim(string.format('%4.1f',mwst/10))
mwst = mwst:gsub('%.',',')
end
error2("generate9004")
self.report_wird_gedruckt:set_visiable(1)
self:interrupt_report_computation("Report wird gedruckt, WAIT26")
error2("generate0001")
self.fehltage_vorhanden = 0 == 1
self.total = 0.00 -- Ticketsumme
self.datum = '' -- Aktuelles
self.datum_raw = '' -- Datum
self.heartbeat_series = {}
self.last_underline = 1
self.zeilen_for_shortreport = {}
self.summe = 0.00
self.heartbeats = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
local has_no_act_datum_line = nil
local alle_report_zeilen_untergebracht = 1
local zaehler = 0
while 0 == 0 do -- gehe durch alle newreport-Zeilen
zaehler = zaehler + 1
self.datum0 = self.datum -- Vorhergehendes
self.datum0_raw = self.datum_raw -- Datum
if zaehler % 10 == 0 then -- alle 10 Zeilen
if file_open ~= nil then
start_timer(26,WAIT_26,0,1)
error2("newreport_0010")
coroutine.yield()
end
end
self:rearrange_spalten(spalten,max_zeilen) -- ggfs. Zeilen schon in die Spalte 2 schaffen
-- error2("newreport_0011")
if #reports == 0 then
break
end
-- error2("newreport_0012")
local stunde = self:fetch_next_report_line(reports,spalten,max_zeilen)
-- error2("newreport_0013")
if self.datum0 ~= self.datum then -- neue Datumsueberschrift anbringen
if #spalten[2] > max_zeilen - 12 then
alle_report_zeilen_untergebracht = 0
break
end
-- for i,z in ipairs(self.heartbeats) do print("UUU",z) end
self:underline_nachgetragen(spalten)
-- error2("newreport_0014")
local fehltage_vorhanden = 0
if self.datum0 ~= '' then
self:add_sum_line(spalten)
fehltage_vorhanden = self:fehltage_eingetragen(reports,spalten,dformat)
end
self:datum_eingetragen(spalten)
-- error2("newreport_0015")
self.summe = 0.00
self.heartbeats = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
has_no_act_datum_line = 1
-- self:rewrite_to_reports(reports) -- Zeile wird zurueckgeschrieben und noch mal bearbeitet
end
-- error2("newreport_0016")
if #spalten[2] > max_zeilen - 8 then
alle_report_zeilen_untergebracht = 0
break
end
-- error2("newreport_0017")
self.heartbeats[stunde] = 1
self:new_report_line(spalten)
if self.current ~= "." then
has_no_act_datum_line = nil
end
-- error2("newreport_0018")
end
self:rewrite_to_reports(reports,has_no_act_datum_line,alle_report_zeilen_untergebracht)
-- error2("newreport_0019")
self:underline_nachgetragen(spalten)
self:add_sum_line(spalten)
-- error2("newreport_0020")
if #self.heartbeat_intervals % 2 == 1 then -- Intervalle muessen immer mit Anfang und Ende angegeben werden
table.remove(self.heartbeat_intervals)
end
local zaehler1 = 0
while 0 == 0 do
zaehler1 = zaehler1 + 1
local zz = self.heartbeat_intervals[zaehler1]
if zz == nil then
break
end
-- print("HB",zz)
end
-- error2("newreport_0021")
if zaehler == 1 or self.contains_real_entries == 0 then -- keine Zeilen verarbeitet
self:reset_squeeze()
return(nil)
end
if #reports == 0 then
if self.squeeze_out == nil then -- um zu verhindern, dass 'halbe' reports ausgedruckt werden
self:reset_squeeze()
return(nil)
end
end
-- error2("newreport_0022")
self:add_netto_ust_total(spalten,max_zeilen)
-- error2("newreport_0023")
return(spalten)
end
--**********************************************************************
function App.rearrange_spalten(self,spalten,max_zeilen)
while #spalten[1] > max_zeilen and #spalten[2] == 0 and #string.gsub(spalten[1][#spalten[1]],' ','') == 0 do
table.remove(spalten[1]) -- Leerzeilen am Ende von Spalte 1 loeschen
end
if #spalten[1] > max_zeilen or #spalten[2] > 0 then -- Zeilen in die zweite Spalte schaffen, wenn noetig
while 0 == 0 do
if
#spalten[1] == 0 or #spalten[1] > max_zeilen
or #spalten[1] > 0 and string.sub(spalten[1][ #spalten[1] ],11,12) == ' -'
or #spalten[1] > 0 and string.sub(spalten[1][ #spalten[1] ],1,2) == '--'
or #spalten[2] > 0 and string.sub(spalten[2][1],1,2) == '--'
or #spalten[2] > 0 and #string.gsub(spalten[2][1],' ','') > 0 and string.sub(spalten[2][1],1,1) == ' '
or #spalten[1] > 0 and #string.gsub(spalten[1][#spalten[1]],' ','') > 0 and string.sub(spalten[1][#spalten[1]],1,1) == ' '
or #spalten[1] > 0 and string.sub(spalten[1][#spalten[1]],3,3) == "." and string.sub(spalten[1][#spalten[1]],6,6) == "."
-- or #spalten[1] > 0 and string.sub(#spalten[1][ #spalten[1] ],6,6) ~= ' '
then
local z = table.remove(spalten[1])
table.insert(spalten[2],1,z)
else
break
end
end
self.nr = 2
if trim(spalten[2][1]) == '' then
table.remove(spalten[2],1)
end
end
end
--**********************************************************************
function App.fetch_next_report_line (self,reports,spalten,max_zeilen)
local zaehler1 = 1
local stunde = nil
while zaehler1 < 100 do -- Format Check fuer Zeilen im newreport.txt
zaehler1 = zaehler1 + 1
self.zeile = table.remove(reports,1) -- hier wird die Zeile geholt
if self.zeile == nil then -- dieser wichtige Fehler wurde erst am 25.8.25 korrigiert
break
end
self.datum_raw = string.sub(self.zeile,1,14) -- im Format yyyymmddHHMMSS
self.current = string.sub(self.zeile,15,15)
if string.sub(self.zeile,16,16) == " " then
self.betrag = trim( string.sub(self.zeile,16) )
else
self.betrag = trim( string.sub(self.zeile,18) ) -- wegen eines Bugs beim Eintrag in die newreport
end
self.betrag_remark = self.betrag
self.betrag = string.gsub(self.betrag,"%a","0") -- der Betrag kann ja auch Buchstaben enthalten von der PIN-Eingabe fuer Freitickets
self.betrag = self.betrag:gsub(',','.')
local x1 = fcheck(-99,tonumber,self.datum_raw)
local x2 = fcheck(-99,tonumber,self.betrag)
if x1 ~= -99 and x2 ~= -99 then
-- print(self.zeile)
stunde = tonumber(string.sub(self.zeile,9,10))+1
-- self.heartbeats[tonumber(string.sub(self.zeile,9,10))+1] = 1
break -- nur weiter, wenn die geholte Zeile korrekt war, sonst einfach naechste Zeile holen
end
end
-- print(self:print_heartbeat())
if stunde ~= nil then
self.uhrzeit = self:date(self.report_data["zformat"],tonumber(self.datum_raw))
self.datum = self:date(self.report_data["dformat"],tonumber(self.datum_raw))
if self.current ~= "." then
table.insert(self.zeilen_for_shortreport,1,{self.datum_raw,self.current,self.betrag,self.betrag_remark})
end
-- print("XV",self.zeile,self.betrag,#self.zeilen_for_shortreport)
-- for i,z in ipairs(self.zeilen_for_shortreport) do print ("WWW",z[1]) end
self:update_heartbeat_intervals()
end
return(stunde)
end
--**********************************************************************
function App.print_heartbeat(self)
local tt = ""
for i,zeile in ipairs(self.heartbeat_intervals) do
tt = tt .. zeile .. ","
end
return(tt)
end
--**********************************************************************
function App.update_heartbeat_intervals (self)
local datum_std = string.sub(self.zeile,1,10)
local akt_stunde = tonumber( string.sub(self.zeile,9,10) )
if self.heartbeat_intervals[1] == "" then -- es ist noch kein Intervall geoeffnet
-- if string.sub(self.datum_raw,1,10) ~= string.sub(self.datum0_raw,1,10) and self.current == "." then
if self.current == "." then
if self.heartbeat_exclude ~= datum_std then -- Intervall oeffnen, aber nur, wenn in der betreffenden Stunde nicht schon ein reales Intervall
if self.heartbeat_intervals[2] ~= datum_std then
self.heartbeat_intervals[1] = datum_std -- aufgemacht wurde
table.insert(self.heartbeat_intervals,1,datum_std)
-- self.datum0 = self.datum -- Vorhergehendes
-- self.datum0_raw = self.datum_raw -- Datum, sonst gibt es Fehler, weil ggfs ein neues Intervall aufgemacht wird
end
end
else
self.heartbeat_exclude = datum_std -- diese Stunde markieren, hier braucht also kein Intervall geoeffnet werden
end
else -- bestehendes Intervall
local akt_stunde0 = tonumber( string.sub(self.heartbeat_intervals[1],9,10) ) -- letzte eingetragene Stunde
local same_day = string.sub(datum_std,1,8) == string.sub(self.heartbeat_intervals[1],1,8)
if akt_stunde == "00" then -- special Berechnung corner case
akt_stunde = "24" -- wenn es der naechste Tag ist, oder die Stunde nicht 23 ist (dann tritt in jedem Fall ein: datum_std- datum_std0 > 1.
same_day = 1 == 1
if akt_stunde0 == "23" then -- hier muss tatsaechlich der Tag geprueft werden
local datum_std0 = string.sub(self.heartbeat_intervals[1],1,10) -- letzte eingetragene Stunde
if self:time(datum_std+"0030") - self:time(datum_std0+"0030") > 86400 + 1000 then -- es ist mehr als einen Tag Differenz
akt_stunde = "25" -- aber diese time-Berechnung will man echt nur machen, wenn es noetig ist
same_day = 0 == 1
end
end
end
-- print("AS",akt_stunde0,akt_stunde)
if same_day and akt_stunde == akt_stunde0 + 1 then
self.heartbeat_intervals[1] = datum_std -- Intervall einfach erweitern
elseif not same_day or akt_stunde ~= akt_stunde0 then
if self.current == "." then
table.insert(self.heartbeat_intervals,1,datum_std) -- da ist eine Luecke, also wirklich ein neues Intervall anfangen
table.insert(self.heartbeat_intervals,1,datum_std)
end
else -- hier gilt: akt_stunde == akt_stunde0
if self.current ~= "." then
if string.sub(self.heartbeat_intervals[2],1,10) == string.sub(self.heartbeat_intervals[1],1,10) then
table.remove(self.heartbeat_intervals,1) -- das angebrochene Intervall kann entfernt werden, da ein realer Eintrag vorliegt,
self.heartbeat_intervals[1] = "" -- und das Intervall erst noch aus einem Punkt besteht
self.heartbeat_exclude = datum_std -- diese Stunde markieren, hier braucht also kein Intervall geoeffnet werden
end
end
end
end
-- for i,z in ipairs(self.heartbeat_intervals) do print("-->",z) end
end
--**********************************************************************
function App.xxadd_to_compact_report (self)
for i,zeile in ipairs(report_data['reports']) do
local datum = tonumber(string.sub(zeile,2,4))
if start_datum == "" then
start_datum = datum
info_data = string.format("%02x",start_datum) -- Startjahr fuer den Report
end -- alle Jahresangaben sind relativ dazu
local tagesnr = ( 12 * (datum - start_datum) + tonumber(string.sub(zeile,5,6)) )
if monatsnr0 ~= monatsnr then
monatsnr0 = monatsnr
zeitplus = 1
end
local tagesminute = 31 * 24 * 60 * monatsnr
+ 24 * 60 * tonumber(string.sub(zeile,7,8))
+ 60 * tonumber(string.sub(zeile,9,10))
+ tonumber(string.sub(zeile,11,12))
-- if zeitplus == 1 then
-- monatsminute *
-- local monatsnr = 12 * (datum - start_datum) + tonumber(string.s
local monatsnr = 12 * (datum - start_datum) + tonumber(string.sub(zeile,5,6))
table.insert(info_data,zeile)
end
end
--**********************************************************************
function App.add_sum_line (self,spalten)
if #spalten[self.nr] > 0 then -- es darf nicht eine Ueberschrift vorausgehen und aber auch keine Leerzeile
if ( not ( string.sub( spalten[self.nr][#spalten[self.nr]], 1,2) == "--" ) ) and
( not ( string.gsub(spalten[self.nr][#spalten[self.nr]]," ","") == "" ) ) then
if self.summe < 9.99 then
table.insert(spalten[self.nr],' ------')
elseif self.summe < 99.99 then
table.insert(spalten[self.nr],' -------')
elseif self.summe < 999.99 then
table.insert(spalten[self.nr],' --------')
else
table.insert(spalten[self.nr],' ---------')
end
table.insert(spalten[self.nr],' '..string.format('%10.2f',self.summe):gsub('%.',',') .. ' ' .. CURRENTSHORT)
end
end
if #spalten[self.nr] > 1 then
table.insert(spalten[self.nr],' ')
end
end
--**********************************************************************
function App.fehltage_eingetragen (self,reports,spalten,dformat)
local datum_midnight_format = string.sub(self.datum_raw,1,8) .. "000001"
local datum_midnight = self:time(datum_midnight_format)
datum_midnight_format = self:date(t("%y%m%d%H%M%S"),datum_midnight)
-- print("==1===",self:print_heartbeat())
local datum0_midnight_format = string.sub(self.datum0_raw,1,8) .. "000001"
local datum0_midnight = self:time(datum0_midnight_format)
if datum0_midnight > 0 and datum_midnight - datum0_midnight > 86400 then
if datum_midnight - datum0_midnight > 2*86400 then
table.insert(spalten[self.nr], self:date(dformat,datum0_midnight+86400) .. " -")
end
self.datum1 = self:date(dformat,datum_midnight-86400)
table.insert(spalten[self.nr],self.datum1)
table.insert(spalten[self.nr],'--========') -- dies zeigt an, dass das Geraet wirklich in diesen Tagen nicht gelaufen ist
table.insert(spalten[self.nr],' ')
-- self:rewrite_to_reports(reports) -- die Zeile in diesem Falle zurueckschreiben in die newreport
-- self.datum0 = self.datum
-- self.datum0_raw = self:date("%y%m%d%H%M%S",datum_midnight-86400)
-- print("DDD",self.datum_raw,self.datum0_raw)
if self.heartbeat_intervals[1] ~= "" and self.heartbeat_intervals[1] ~= string.sub(self.datum_raw,1,10) then
table.insert(self.heartbeat_intervals,1,"") -- weil es ja mindestens einen ganzen Fehltag gibt, muss ein neues
end -- Intervall angefordert werden
-- print("==2===",self:print_heartbeat())
return(1)
end
return(0)
end
--**********************************************************************
function App.rewrite_to_reports (self,reports,has_no_act_datum_line,alle_report_zeilen_untergebracht)
if #self.heartbeat_intervals > 1 and self.heartbeat_intervals[1] == "" then
table.remove(self.heartbeat_intervals,1)
end
self.debug1:write(tostring(has_no_act_datum_line)..tostring(alle_report_zeilen_untergebracht)..self.zeile)
if alle_report_zeilen_untergebracht == 0 then
table.insert(reports,1,self.zeile)
end
if self.current ~= "." then
if has_no_act_datum_line == nil then
table.remove(self.zeilen_for_shortreport,1)
end
end
end
--**********************************************************************
function App.underline_nachgetragen (self,spalten)
for i = 1,2,1 do
if spalten[i] ~= nil and #(spalten[i]) >= self.last_underline then
if spalten[i][self.last_underline] == "DDD" then
local datum_underline = "--"
local zaehler = 0
local stunden = self.heartbeats
while zaehler < 24 do
zaehler = zaehler + 3
-- for i,z in ipairs(self.heartbeats) do print("UUU",z) end
-- print("QQU",stunden[zaehler-2],2*stunden[zaehler-1],4*stunden[zaehler],zaehler,1+stunden[zaehler-2]+2*stunden[zaehler-1]+4*stunden[zaehler])
datum_underline = datum_underline .. self.symbols_for_underlining[1+stunden[zaehler-2]+2*stunden[zaehler-1]+4*stunden[zaehler]]
end
-- datum_underline = "----------" -- zu Testzwecken das detaillierte Underlining ausschalten
spalten[i][self.last_underline] = datum_underline
return
end
end
end
for i,zeile in ipairs(spalten[2]) do -- falls sich die Angabe last_underline wegen rearrange_spalten verschoben hat
if zeile == "DDD" then
self.last_underline = i
self:underline_nachgetragen(spalten)
break
end
end
end
--**********************************************************************
function App.datum_eingetragen (self,spalten)
-- datum_underline = "----------" -- zu Testzwecken das detaillierte Underlinig ausschalten
if self.only_heartbeat == 0 or datum_underline ~= "----------" then -- nur Datumsueberschrift machen, wenn es mehr als Heartbeat-Eintraege gibt, oder Heartbeats fehlen
table.insert(spalten[self.nr],self.datum) -- jetzt die Datumsueberschrift
table.insert(spalten[self.nr],"DDD") -- und das Underlining, welches gleichzeitig Heartbeat-Luecken zeigt, das kann aber erst im Nachhinein eingetragen werden
self.last_underline = #(spalten[self.nr])
end
end
--**********************************************************************
function App.new_report_line (self,spalten)
self.betrag = tonumber(self.betrag)
self.summe = self.summe + self.betrag
self.total = self.total + self.betrag
if self.betrag < 0.00000001 then
self.betrag = " " .. trim(self.betrag_remark)
else
self.betrag = string.format('%7.2f',self.betrag)
self.betrag = self.betrag:gsub('%.',',')
end
if self.current ~= "." then -- bei current = "." handelt es sich nur um einen Heartbeat-Eintrag, also nicht beruecksichtigen
table.insert(spalten[self.nr],self.uhrzeit..self.betrag..' '..self.current) -- hier wird die Ticket-Zeile eingetragen
contains_real_entries = 1
end
end
--**********************************************************************
function App.add_netto_ust_total (self,spalten,max_zeilen)
local umsatzsteuersatz = fcheck(0,tonumber,self.tax_vat:read())
if umsatzsteuersatz == nil then
umsatzsteuersatz = 0
end
umsatzsteuersatz = self:sync_report_data("tax_vat",umsatzsteuersatz)
local usteuer = umsatzsteuersatz / ( 100 + umsatzsteuersatz ) * self.total
local netto = 100 / ( 100 + umsatzsteuersatz ) * self.total
if self.nr == 1 and #spalten[1] > max_zeilen - 9 or #spalten[2] < 2 then
self.nr = 2
end
if #spalten[self.nr] > 0 then
if #string.gsub(spalten[self.nr][#spalten[self.nr]],' ','') > 0 then
table.insert(spalten[self.nr],'')
end
end
umsatzsteuersatz = (umsatzsteuersatz * 1000 + 1)/100
if math.floor(umsatzsteuersatz % 10) == 0 then
umsatzsteuersatz = string.format('%2u',math.floor(umsatzsteuersatz/10))
else
umsatzsteuersatz = trim(string.format('%4.1f',umsatzsteuersatz/10))
umsatzsteuersatz = umsatzsteuersatz:gsub('%.',',')
end
local o1 = string.format('%9.2f',netto):gsub('%.',',')
table.insert(spalten[self.nr],"NETTO"..o1:gsub(" ",":",1).."")
if #umsatzsteuersatz == 2 then
o1 = string.format('%9.2f',usteuer):gsub('%.',',',1)
table.insert(spalten[self.nr],t('UST',tn)..umsatzsteuersatz..o1:gsub(" ","%%",1).."" )
elseif usteuer < 1000.00 then
table.insert(spalten[self.nr],t('UST',tn)..umsatzsteuersatz..'%' .. string.format('%6.2f',usteuer):gsub('%.',','))
else
table.insert(spalten[self.nr],t('UST',tn)..umsatzsteuersatz .. string.format('%7.2f',usteuer):gsub('%.',','))
end
o1 = string.format('%9.2f',self.total):gsub('%.',',',1)
table.insert(spalten[self.nr],'TOTAL'..o1:gsub(" ",":",1).."")
end
--**********************************************************************
function App.make_signa_report (self,text1,text2,reportnr)
local text = ''
if text2 == "" then -- fuer Tests
text = text1
else
for i,zeile in ipairs(text1) do
text = text .. zeile
end
for i,zeile in ipairs(text2) do
text = text .. zeile
end
text = text:gsub(' ','') -- dies ist der Text des Reports, der zum Signieren verwendet wird
end
-- print("SIGNTEXT",text)
local md5_report = ( md5lib.hash(text:gsub(' ','')) )
error2("short0001")
local md5_crypt_hex = self:crypt(sec_key, md5_report)
error2("short0002")
local md5_crypt_bin = bin.hextos( md5_crypt_hex )
-- print(md5_crypt_bin)
error2("short0003")
self.signature = base27_encode( md5_crypt_bin )
-- print("SIG",self.signature,#self.signature)
local signa_short = string.sub(md5_crypt_hex,2,3) .. string.sub(md5_crypt_hex,10,11) .. string.sub(md5_crypt_hex,16,17)
error2("short0004")
-- print("Short",signa_short)
return(signa_short)
end
--**********************************************************************
function App.tagesnr (self,aktdate)
return( math.floor ( aktdate / 86400 ) )
end
--**********************************************************************
function App.make_short_report (self)
local text1 = ""
local alfa = "abcdefghijklmnopqrstuvwxyz"
local loc1 = bin.stohex( self.location1:read() )
local whoadm = bin.stohex( self.report_data['whoadmin'] )
local reportstatic = string.format("%02x",self.report_data['lang']) .. string.format("%04x",10*tonumber(self.report_data['tax_vat'])) .. loc1 .. whoadm
local reporthash = self:hash_from_reportstatic(reportstatic)
if not string.match(self.reporthashes,","..reporthash..",") then -- der Hash existiert schon, d.h. es gibt schon einen QR-Export, der die Daten enthaelt
self.reporthashes0 = self.reporthashes
self.reporthashes = self.reporthashes .. reporthash .. ","
reporthash = "f0" .. string.format("%02x",#loc1/2) .. string.format("%02x",#whoadm/2) -- dieser Dummy Reporthash gibt Laengen an, > 8fffff
else
reportstatic = '' -- diese Information wird ja nicht mehr gebraucht (reporthash <= 8fffff)
end
text1 = text1 .. string.format("%010x",tonumber(self.report_data["reportdatum"])) -- 5 Byte
text1 = text1 .. string.format("%04x", tonumber(self.report_data["reportnr"])) -- 2 Byte
text1 = text1 .. reporthash -- 3 Byte Hash der uebrigen statischen Felder
text1 = text1 .. reportstatic -- falls der String noch nicht existiert, hier explizit angeben
text1 = text1 .. self.report_data["signastamp"] -- 3 Byte
text1 = text1 .. string.format("%02x",self.report_data["maxzeilen"]%256) -- 1 Byte
-- print("TTT",#text1,text1)
local last_day = self:tagesnr( tonumber(self.report_data["reportdatum"]) - 1600000000 )
local z1 = 0
while 0 == 0 do
z1 = z1 + 1
if z1 % 10 == 0 then
self:interrupt_report_computation("Loop for short report, WAIT26")
end
error2("shortreport0001")
-- 1. Byte:
-- Bit 1: 0 Betrags-Eintrag, 1 Alt Mode
-- Bit 2: mit Zusatzbyte f Betrag (bei Alt Mode: 0: Heartbeat Intervallangabe, 1: Kuerzel-Eintrag bzw Short Betrag)
-- Bit 3: mit Zusatzbyte(s) f Zeit (die ein oder zwei Bytes sind eine Addition von Tagen)
-- Bit 4/5: Bei Alt Mode und Kuerzel: die ersten zwei Bits des Kuerzel-Eintrags,
-- Bei Alt Mode und Heartbeat: zusammen mit Bit 6-8 Angabe der Tagesstunde
-- Bei Normal Mode: 00: E, 01: Q
-- Bit 6-8: ersten drei Bits für Angabe der Minute des Tages.
-- 2. Byte: Zeit (weitere 8 Bits, also insgesamt 11 Bits fuer Minute des Tages)
-- 3. Byte: Betrag (bzw. bei Alt Mode die naechsten 8 Bit des Kuerzel-Eintrags)
-- 4.-6. Byte: Zusatzbyte Betrag plus Zusatzbytes Zeit
local zeile1 = self.heartbeat_intervals[ #self.heartbeat_intervals ]
-- if zeile1 == "" then
-- zeile1 = nil
-- end
if zeile1 ~= nil then
zeile1 = zeile1 .. "0005"
end
local zeile2 = self.zeilen_for_shortreport[ #self.zeilen_for_shortreport ]
local current
local betrag
local remark
if zeile2 ~= nil then
current = zeile2[2]
betrag = zeile2[3]
remark = zeile2[4]
zeile2 = zeile2[1]
end
local zeile
error2("shortreport1001")
if zeile1 == nil then
if zeile2 == nil then
break
else
zeile = zeile2
table.remove(self.zeilen_for_shortreport)
end
else
if zeile2 == nil then
zeile = zeile1
current = "."
table.remove(self.heartbeat_intervals)
else
if tonumber(zeile1) < tonumber(zeile2) then
zeile = zeile1
current = "."
table.remove(self.heartbeat_intervals)
else
zeile = zeile2
table.remove(self.zeilen_for_shortreport)
end
end
end
-- print(zeile,zeile1,zeile2,#self.zeilen_for_shortreport)
local byte1 = 0
local akt_day = self:tagesnr( self:time(string.sub(zeile,1,14)) )
local time_diff = akt_day - last_day
last_day = akt_day
local tagesminute = 60 * tonumber(string.sub(zeile,9,10)) + tonumber(string.sub(zeile,11,12))
local byteminute = string.format("%03x",tagesminute)
local byte2 = string.sub(byteminute,2,3)
local betragbyte1 = ""
local betragbyte2 = ""
local zeitbyte1 = ""
local zeitbyte2 = ""
error2("shortreport1002")
if time_diff > 0 then -- Additionstage muessen angegeben werden
byte1 = byte1 + 1*32 -- 3. Bit zeigt an, dass es Additionstage gibt
zeitbyte1 = time_diff % 128
zeitbyte2 = math.floor(time_diff/128)
if zeitbyte2 > 0 then
zeitbyte1 = zeitbyte1 + 1*128 -- erstes Bit sagt, dass noch ein weiteres Bit kommt
zeitbyte2 = string.format("%02x",zeitbyte2)
else
zeitbyte2 = ""
end
zeitbyte1 = string.format("%02x",zeitbyte1)
end
error2("shortreport1003")
if current == "." then -- heartbeat interval. Hier werden die Stunden angegeben, Minuten braucht man nicht
byte1 = byte1 + 1*128 -- Alt Mode
byte1 = byte1 + 0*64 -- ist Heartbeat-Intervallangabe
local tagesstunde = tonumber(string.sub(zeile,9,10))
-- print("TAGESSTUNDE",tagesstunde)
byte1 = byte1 + tagesstunde
-- print("BBB",byte1)
byte2 = ""
else
byte1 = byte1 + tonumber("0x"..string.sub(byteminute,1,1))
if current == "X" then
byte1 = byte1 + 1*128 -- Alt Mode
byte1 = byte1 + 1*64 -- Kuerzel Eintrag
-- print("BBB",remark,self.zeile)
remark = string.sub(remark,#remark-1,#remark)
if string.find(remark,"%D") == nil then -- wenn doch ein Betrag angegeben ist
remark = 900 + tonumber(remark) -- um das von einer Buchstaben-Kodierung zu unterscheiden
else -- wenn ein Buchstaben Kuezel gegeben ist
remark = (string.find(alfa,string.sub(remark,1,1))-1) * 26 + (string.find(alfa,string.sub(remark,2,2))-1)
end
local bytebetrag = string.format("%03x",remark)
-- print("RRR",remark,bytebetrag,byte1,string.format("%0x",byte1))
byte1 = byte1 + tonumber(string.sub(bytebetrag,1,1)) * 8 -- die ersten zwei Bits des zehnstelligen Kuerzel-Betrags-Bytes
betragbyte1 = string.sub(bytebetrag,2,3)
-- print(bytebetrag,betragbyte1,string.format("%0x",byte1))
else
if current == "Q" then
byte1 = byte1 + 1*8
else
byte1 = byte1 + 0*8
end
betrag = math.floor(betrag * 100 / self.ggt + 0.0001)
betragbyte1 = betrag % 256
betragbyte2 = math.floor(betrag/256)
if betragbyte2 > 0 then
byte1 = byte1 + 1*64 -- zeigt an, dass es ein Zusatzbyte fuer den Betrag gibt
betragbyte2 = string.format("%02x",betragbyte2)
else
betragbyte2 = ""
end
-- print(self.ggt,betrag,betragbyte1,betragbyte2)
betragbyte1 = string.format("%02x",betragbyte1)
end
end
error2("shortreport1004")
byte1 = string.format("%02x",byte1)
-- print("ENTRY",zeile,current,betrag,"TAG",akt_day,time_diff,"---",byte1,byte2,".",betragbyte1,betragbyte2,zeitbyte1,zeitbyte2)
text1 = text1 .. byte1 .. byte2 .. betragbyte1 .. betragbyte2 .. zeitbyte1 .. zeitbyte2
end
-- print("WWER",#text1,text1)
error2("shortreport0001a")
local text1_bin = bin.hextos( text1 )
error2("shortreport0001b")
local text1_crypt = self:crypt(sec_key, text1_bin)
error2("shortreport0001c")
self:interrupt_report_computation("Loop for short report, WAIT26")
error2("shortreport0002")
local text1_crypt_bin = bin.hextos( text1_crypt )
self:interrupt_report_computation("Loop for short report, WAIT26")
error2("shortreport0003")
local text1_crypt_enc = base62_encode( text1_crypt_bin )
self:interrupt_report_computation("Loop for short report, WAIT26")
error2("shortreport0004")
local text1_cry32_enc = base32_encode( text1_crypt_bin )
-- print(text1_cry32_enc)
local sr32 = {}
while text1_cry32_enc ~= '' do
local t1 = string.sub(text1_cry32_enc,1,24)
text1_cry32_enc = string.sub(text1_cry32_enc,25)
local nr1 = 1 + #sr32 % 32 -- Zeilen Zaehler
t1 = t1 .. string.sub(CHAR32,nr1,nr1)
local t2 = bin.stohex( md5lib.hash(t1) )
t2 = tonumber("0x"..string.sub(t2,1,3)) % 1000
local nr2 = 1 + math.floor(t2/32)
local nr3 = 1 + t2 % 32
table.insert(sr32, string.sub(
string.sub(t1,1,5) .. " " .. string.sub(t1,6,10) .. " " .. string.sub(t1,11,15) .. " " ..
string.sub(t1,16,20) .. " " .. string.sub(t1,21,25) ..
" ",1,29) .. " " ..
string.sub(CHAR32,nr2,nr2) .. string.sub(CHAR32,nr3,nr3) )
end
error2("shortreport0005")
local text1_crypt_enc_with_sno_class = string.sub(text1_crypt_enc,1,10) .. SE_CLASS .. string.sub(text1_crypt_enc,11)
return text1_crypt_enc_with_sno_class,sr32
end
--**********************************************************************
function App.hash_from_reportstatic (self,reportstatic)
local reporthash = bin.stohex( md5lib.hash(reportstatic) )
reporthash = string.format("%02x", tonumber( "0x" .. string.sub(reporthash,1,2) ) % 128 ) .. string.sub(reporthash,3,6)
return(reporthash)
end
--**********************************************************************
function App.unfold_report (self,text2,report_data) -- stellt erstmal eine dummy newreport-Datei her, die danach als Report formatiert werden kann
if SE_CLASS ~= string.sub(text2,11,12) then
return("---Wrong_Serialnumber_class---")
end
local text1_crypt_enc = string.sub(text2,1,10) .. string.sub(text2,13)
local text1_crypt_bin = base62_decode( text1_crypt_enc )
local text1_crypt = bin.stohex( text1_crypt_bin )
local text1_bin = self:decrypt(pub_key, text1_crypt )
local text1 = bin.stohex( text1_bin )
local alfa = "abcdefghijklmnopqrstuvwxyz"
if #text1 < 10 then
return("---No_valid_transmit_text---")
end
report_data["reportdatum"] = tonumber("0x"..string.sub(text1,1,10))
report_data["reportnr"] = string.format("%1u",tonumber("0x"..string.sub(text1,11,14)))
local reporthash = string.sub(text1,15,20)
local rdata = {}
if string.sub(reporthash,1,1) == "f" then -- Statische Daten werden mitgegeben
local loc1_len = tonumber("0x" .. string.sub(text1,17,18))
local whoadm_len = tonumber("0x" .. string.sub(text1,19,20))
local reportstatic = string.sub( text1,21,27+2*loc1_len+2*whoadm_len )
local reporthash = self:hash_from_reportstatic(reportstatic)
text1 = string.sub( text1,27+2*loc1_len+2*whoadm_len )
rdata["lang"] = tonumber("0x" .. string.sub(reportstatic,1,2))
rdata["tax_vat"] = string.format("%3.1f", tonumber("0x" .. string.sub(reportstatic,3,6)) / 10 )
rdata["location"] = bin.hextos( string.sub(reportstatic,7,7+2*loc1_len-1) )
rdata["whoadmin"] = bin.hextos( string.sub(reportstatic,7+2*loc1_len,7+2*loc1_len+2*whoadm_len-1) )
else
rdata = self.static_strings[reporthash]
if rdata == nil then
return("---No_static_data---")
end
text1 = string.sub(text1,21)
end
report_data["signastamp"] = string.sub(text1,1,6)
report_data["maxzeilen"] = string.format("%010u",tonumber("0x"..string.sub(text1,7,8)))
text1 = string.sub(text1,9)
local tagesdatum = tonumber(report_data["reportdatum"]) - 1600000000
tagesdatum = 86400 * math.floor(tagesdatum/86400) + 1
local newreport = {}
local heartbeats = {}
local entry_hours = {} -- diese Stunden brauchen nicht durch Heartbeats abgedeckt zu werden, weil reale Entries vorliegen
while 0 == 0 do -- ab hier alle Eintraege in der text1 evaluieren
-- print("TT",string.sub(text1,1,2))
local byte1 = bin.hextobin( string.sub(text1,1,2) )
local betrag = ""
local betragnr = 0
local kuerzel = ""
local add_tag = 0 -- Tagesdifferenz
local is_neartbeat = 0
local current = CURRENTSHORT
if byte1[5] == "1" then
current = "Q"
end
local tagesminute
if byte1[1] == "0" or byte1[2] == "1" then -- Normal mode oder Alt mode mit Kuerzel Angabe
betrag = string.sub(text1,5,6)
tagesminute = tonumber(byte1[6])*1024 + tonumber(byte1[7])*512 + tonumber(byte1[8])*256 + tonumber("0x"..string.sub(text1,3,4))
text1 = string.sub(text1,7)
else -- das ist dann ein Heartbeat-Eintrag, d.h. nur ein einziges Byte plus evtl. Zeit-Byte(s)
tagesminute = tonumber(byte1[4])*16 + tonumber(byte1[5])*8 + tonumber(byte1[6])*4 + tonumber(byte1[7])*2 + tonumber(byte1[8])
-- print("HBB",tagesminute)
tagesminute = tagesminute * 60 + 5
is_heartbeat = 1
current = "."
text1 = string.sub(text1,3) -- das ist dann ein Heartbeat-Eintrag, also Haupteintrag nur ein einziges Byte
end
if byte1[2] == "1" then
if byte1[1] == "0" then -- Normal-Mode: ein Zusatzbyte f Betrag noetig
betrag = betrag .. string.sub(text1,1,2)
text1 = string.sub(text1,3)
else -- Alt-Mode: Kuerzel-Eintrag
local kuerzelnr = tonumber(byte1[4])*512 + tonumber(byte1[5])*256 + tonumber("0x"..betrag)
if kuerzelnr > 900 - 1 then
betragnr = kuerzelnr - 900
else
local n1 = 1 + math.floor(kuerzelnr/26)
local n2 = 1 + kuerzelnr%26
kuerzel = string.sub(alfa,n1,n1) .. string.sub(alfa,n2,n2)
-- print("KNK",byte1[4],byte1[5],betrag,kuerzelnr,n1,n2,kuerzel)
end
current = "X"
betrag = ""
end
end
if byte1[3] == "1" then -- es gibt einen Wechsel des Tages
add_tag = tonumber( "0x" .. string.sub(text1,1,2) )
if add_tag > 127 then
add_tag = (add_tag - 128) + 128 * tonumber( "0x" .. string.sub(text1,3,4) )
text1 = string.sub(text1,5)
else
text1 = string.sub(text1,3)
end
end
tagesdatum = tagesdatum + 86400*add_tag
local stdmin = string.format("%02u",math.floor(tagesminute/60)) .. string.format("%02u",tagesminute%60)
local datum_formatted = self:date("%y%m%d",tagesdatum) .. stdmin .. "10"
if betrag ~= "" then
betrag = string.format("%6.2f",self.ggt*tonumber("0x"..betrag)/100+0.0001)
elseif kuerzel ~= "" then
betrag = " 0," .. kuerzel
else
betrag = string.format("%6.2f",betragnr/100+0.0001)
end
betrag = string.gsub(betrag,"%.",",")
if current == "." then
-- print("HBC",tagesdatum,tagesminute)
table.insert(heartbeats,tagesdatum+tagesminute*60+10)
else
table.insert(newreport,datum_formatted..current .. " " .. betrag)
entry_hours[string.sub(datum_formatted,1,10)] = 1
end
if current ~= "" then
print(#text1,tagesminute,datum_formatted..current,betrag)
end
if #text1 == 0 then
break
end
end
print("-----")
local heartbeats_expanded = self:expand_heartbeats(heartbeats,entry_hours)
-- for i,z in ipairs(heartbeats_expanded) do print("ddd",z) end
local newreport_with_heartbeats = self:merge_heartbeats_to_newreport(heartbeats_expanded,newreport)
-- print("NN",newreport_with_heartbeats)
return(newreport_with_heartbeats)
end
--**********************************************************************
function App.recreate_report(self,short_report) -- aus einem short_report wieder den Report rekonstruieren
-- diese Funktion wird nicht im Geraet eingesetzt
MAXTRANSMIT0 = MAXTRANSMIT
MAXTRANSMIT = 400
self.report_data = {}
local xx = self:unfold_report(self.sr,self.report_data)
self.alt_report = "xxx\n---\n" ..xx
self.report_data['reports'] = nil
self.report_data['signastamp'] = nil
self.squeeze_out = 1
local filename1 = xx
if string.sub(xx,1,3) ~= "---" then
filename1 = self:p_generate_report()
print("-----------")
end
MAXTRANSMIT = MAXTRANSMIT0
self.alt_report = nil
return(filename1)
end
--**********************************************************************
function App.expand_heartbeats (self,heartbeats,entry_hours)
local heartbeats_expanded = {}
while 0 == 0 do
local a1 = table.remove(heartbeats,1)
if a1 == nil then
break
end
local a2 = table.remove(heartbeats,1)
-- print("HBEX",a1,a2)
while 0 == 0 do
if a1 > a2 then
break
end
local a1_formatted = self:date("%y%m%d%H%M%S",a1)
-- print(">>",a1_formatted)
if entry_hours[string.sub(a1_formatted,1,10)] == nil then
table.insert(heartbeats_expanded,a1_formatted..". 0,00")
end
a1 = a1 + 3600
-- print(a1,a2)
end
end
return(heartbeats_expanded)
end
--**********************************************************************
function App.merge_heartbeats_to_newreport (self,heartbeats_expanded,newreport)
local newreport1 = {}
while 0 == 0 do
local zeile1 = nil
local zeile2 = nil
if #heartbeats_expanded > 0 then
zeile1 = heartbeats_expanded[#heartbeats_expanded]
end
if #newreport > 0 then
zeile2 = newreport[#newreport]
end
if zeile1 == nil then
if zeile2 == nil then
break
else
table.insert(newreport1,1,table.remove(newreport))
end
else
if zeile2 == nil then
table.insert(newreport1,1,table.remove(heartbeats_expanded))
else
-- print(zeile1,zeile2)
if tonumber(string.sub(zeile1,1,14)) < tonumber(string.sub(zeile2,1,14)) then
table.insert(newreport1,1,table.remove(newreport))
else
table.insert(newreport1,1,table.remove(heartbeats_expanded))
end
end
end
end
newreport2 = ""
for i,z in ipairs(newreport1) do newreport2 = newreport2 .. z .. "\n" end
return(newreport2)
end
--**********************************************************************
function App.read_qr_reports (self)
end
--**********************************************************************
function App.test1_generate_report (self,opt_par)
-- self.alt_report = ""
local text2 = {}
local rs = 1
if file_open ~= nil then
rs = tonumber(self:time())*1000+tonumber(get_timer_value(29))
else
rs = math.floor ( (self:time()*10000+os.clock()*1000000000000+tonumber(3434)) % 9999 )
end
-- rs = 2141
math.randomseed(rs)
print("RANDOMSEED",rs)
local zaehler = 0
zaehler = 1
st = 1731866900 + tonumber(3434)
while zaehler < 110 do
st = st + math.random(1000,5000)
if math.random(1,10) == 1 then
st = st + math.random(1000000,5000000)
end
cu = math.random(1,10)
cu = string.sub(CURRENTSHORT.."QX"..CURRENTSHORT..CURRENTSHORT..CURRENTSHORT.."....",cu,cu)
if cu == "." then
betrag = " 0,00"
elseif cu == "X" then
betrag = ({"0,as","0,ph","0,tx","0,fg"})[math.random(1,4)]
else
betrag = string.gsub(string.format("%5.2f",math.random(2,90)/10),"%.",",")
end
local zeile1 = self:date("%y%m%d%H%M%S",st) .. cu .. " " .. betrag
table.insert(text2,zeile1)
-- self.alt_report = self.alt_report .. self:date("%y%m%d%H%M%S",st) .. cu .. " " .. betrag .. "\n"
zaehler = zaehler + 1
end
-- print("AAS",self.alt_report)
self.report_is_integer = 1
text20 = {}
text202 = [[20241117194138. 0,00
20241117204212M 4,20
20241117212319. 0,00
20241117220121. 0,00
20241117222708M 4,60
20241117233736X 0,fg
20241118005234. 0,00
20241118011002M 6,90
20241118021503M 0,30
20241118025446. 0,00
20241118041657Q 3,30
20241118053309. 0,00
20241118063230. 0,00
20241118075338M 7,00
20241118083034. 0,00
20241230192821Q 2,30
20241230200201. 0,00
20250225224203M 7,00
20250225234232. 0,00
20250226005200. 0,00
20250226015934M 5,10
20250226022506. 0,00
20250226031049M 0,40
20250226043012M 5,60
20250226051135. 0,00
20250226054915. 0,00
20250226062816Q 1,10
20250226074638M 6,50
20250226081145M 4,20
20250406013051. 0,00
20250419150302. 0,00
20250419152302. 0,00
20250419154818X 0,fg
20250419163509. 0,00
20250419171121M 4,20
20250419173041. 0,00
20250419175342M 9,00
20250419181845M 1,60
20250419184138. 0,00
20250419190557. 0,00
20250419193336M 2,80
20250419204343. 0,00
20250419211720M 5,00
20250419215624M 3,50
20250419224013M 0,60
20250420000244Q 4,60
20250420011433M 0,30
20250420014602X 0,fg
20250420030034M 1,60
20250420033418. 0,00
20250420042500M 0,70
20250420054243M 2,70
20250420061718. 0,00
20250420065522X 0,ph
20250609133946M 0,70
20250609140016M 8,10
20250609145927X 0,fg
20250609151724. 0,00
20250625051647M 8,50
20250625063636X 0,as
20250625073622M 5,80
20250711215922X 0,fg
20250711222740. 0,00
20250711234717. 0,00
20250712004244M 3,50
20250712012736M 8,60
20250712022049M 4,40
20250712024522. 0,00
20250712032419. 0,00
20250712043904. 0,00
20250712055615Q 7,10
20250712070258. 0,00
20250712080558M 2,60
20250712085938. 0,00
20250712102131. 0,00
20250712114015. 0,00
20250712122822M 6,10
20250712134446M 5,40
20250712141720M 1,50
20250712151023. 0,00
20250712154649. 0,00
20250712170935Q 2,50
20250712182625. 0,00
20250712194147. 0,00
20250712205233M 2,70
20250712220531. 0,00
20250712231133M 3,10
20250712234305. 0,00
20250713002423M 2,80
20250713005823. 0,00
20250729080222M 8,80
20250729091858. 0,00
20250729103227. 0,00
20250729112305. 0,00
20250729120321. 0,00
20250729122618M 4,00
20250729131759X 0,fg
20250729142250Q 8,90
20250729151409X 0,tx
20250729154536M 1,20
20250729161534. 0,00
20250901144553M 0,40
20250901155819. 0,00
20250901171703X 0,ph
20251012112742M 6,80
20251012121249. 0,00
20251012133236. 0,00
20251012142501. 0,00
20251012144815. 0,00]]
for zeile in string.gmatch(text202,'[^\n]+') do
table.insert(text20,zeile)
end
-- text2 = text20
if opt_par ~= nil then
-- local text1_crypt = self:crypt(sec_key, self.alt_report)
-- local text1_crypt_bin = bin.hextos( text1_crypt )
-- local text1_crypt_enc = base62.encode( string.sub(text1_crypt_bin,1,2000) )
print(text2[1])
self:write_report(text2)
else
self.lang_nr_for_report = 1
self.alt_report = "xxx\n---\n" -- zufaellige newreport-daten
for i,zeile in ipairs(text2) do
self.alt_report = self.alt_report .. zeile .. "\n"
end
local filename = self:p_generate_report()
os.execute("lua main.lua read "..filename) -- daraus File und shortreport machen
-- print(self.sr)
local filename1 = self:recreate_report(self.sr) --- und gleich wieder zurueckrechnen
print("-----------"..filename1)
-- if string.sub(filename1,1,3) ~= "---" then
os.execute("lua main.lua read "..filename1)
-- else
-- print("No file generated. "..filename1.."\n")
-- end
end
end
--**********************************************************************
function App.test1_print_big_qrcode (self,opt_par,nr)
local test3302 = {
'QQx1b@',
'QQx1d(k40QQx31QQx41QQx31QQx00',
'QQx1d(k30QQx31QQx43QQx04',
'QQx1d(k30QQx31QQx45QQx34',
'QQx1d(k30QQx31QQx51QQx30',
'QQx1d(kQQxe8QQx00QQx31QQx50QQx30',
'QQhttps://cx.12park.de/RRR00OPp30SE2MS1Q02V003VKv129462379463791462T88ZsHLqOl3z1',
'rAKmc27UGeZjqPuqkLKiIwBFVd9YO928mB7MP4meptdbbz9g9cDmp5cbC3vSUgFuY018',
'GostweQRZW01ePOSslKydxb17E6z8vvDdokM3PNy502x5qLq2ruNg',
'rU66gaoHAN00JOadQGAnMEn014f9hXa9imvB8y011J01gfdgfrgfdgfdsghhgfd02',
'QQx1d(k30QQx31QQx51QQx30',
'QW',
'QW',
' ',
}
self:send_to_printer1(test3302)
end
--**********************************************************************
function App.test1_simu_taste (self)
-- print("112233 ------")
if #self.tastenfolge == 1 then
self:test1_new_tastenfolge()
self:test1_simu_taste()
elseif #self.tastenfolge ~= 0 then
local factor = 1
local akt_pause = table.remove(self.tastenfolge,1)
ACT_TASTE = table.remove(self.tastenfolge,1)
if self.simu_par == "03306" then
if ACT_TASTE ~= 999999 then
akt_pause = akt_pause / 10
end
if akt_pause > 60000 then
akt_pause = 5500
end
end
print(akt_pause,ACT_SCREEN,ACT_TASTE)
-- error1("taste"..tostring(ACT_TASTE).." " ..tostring(akt_pause))
start_timer(8,akt_pause/factor,0,1)
end
end
--**********************************************************************
function App.test1_new_tastenfolge (self)
local par = self.tastenfolge[1]
if file_open ~= nil then
rs = tonumber(self:time())*1000+tonumber(get_timer_value(29))
else
rs = math.floor ( (self:time()*10000+os.clock()*1000000000000+tonumber(3434)) % 9999 )
end
-- rs = 2141
math.randomseed(rs)
-- if SIMUDATA == nil then
-- local rr = my_read_filedata1('simudata.txt',sd_dir..'/simudata.txt')
-- if rr ~= nil then
-- rr = load(rr)
-- SIMUDATA = rr()
-- else
-- SIMUDATA = 0
-- end
-- end
SIMUDATA = nil
if par == 101 then
local zufallswerte = {2911,3482,4555,5723,2989,
-- 3322,4777,5922,6233,4771,
-- 4927,7432,4662,5371,5725,
-- 4927,7432,4662,5371,5725,
-- 4927,7432,4662,5371,5725,
90002,3803}
local zaehler = 0
local tt
local mm
self.tastenfolge = {}
if SIMUDATA ~= nil and SIMUDATA ~= 0 then
self.tastenfolge = table.remove(SIMUDATA,1)
print("SD",SIMUDATA)
if self.tastenfolge == nil then
self.tastenfolge = {}
end
end
if #self.tastenfolge == 0 then
while zaehler < math.random(5,10) do
zaehler = zaehler + 1
table.insert(self.tastenfolge,math.random(800,4000))
table.insert(self.tastenfolge,math.random(1081,1085))
end
self.tastenfolge[ 2 ] = math.random(1,3)
self.tastenfolge[ #self.tastenfolge ] = 80
table.insert(self.tastenfolge, zufallswerte [ math.random(1,#zufallswerte) ] )
table.insert(self.tastenfolge, 999999)
table.insert(self.tastenfolge, 101)
end
local atime = self:time()
local dtime = string.sub(self:date("%y%m%d",atime),3,8) .. "_" .. self:date("%H%M%S",atime)
local text1 = ''
zaehler = 0
for i,number in ipairs(self.tastenfolge) do
zaehler = zaehler + 1
text1 = text1 .. string.format("%9u",number)
if zaehler % 2 == 0 then
text1 = text1 .. "\n"
else
text1 = text1 .. " "
end
end
local readcount = string.format("%04u",self:read_counter())
local testdata_file = "testdata__" .. dtime .. "__" .. readcount .. ".txt"
my_write_filedata1(testdata_file,sd_dir..'/'..testdata_file,text1 .. "\n",over_write)
local text2 = self:decrypt( pub_key3, my_read_filedata1('newreport.txt',sd_dir..'/newreport.txt') )
local newreport_file = "newreport_" .. dtime .. ".txt"
my_write_filedata1(newreport_file,sd_dir..'/'..newreport_file,text2 .. "\n",over_write)
end
end
--**********************************************************************
function xxtranscode (b1,b2,r1,r2,s)
erg = ""
act_s = 0
x = 0
while (0 == 0) do
x = x*f1 + r1[string.sub(s,1,1)]
s = string.sub(s,2)
x0 = x
while (0 == 0) do
while (x0 > #b2) do
x0 = x0 // #b1
end
if x0 > #b1 then
end
end
end
end
--**********************************************************************
lensym = 1
function App.qr_to_bitmap (self,faktor,qrdata,mode) -- zeilenweises abarbeiten der qrcode-daten
local erg = {}
local erg1 = {}
local faktor1 = faktor
local faktor2 = faktor
local xreduce = 0
local yreduce = 0
local yloop = 3
if faktor1 % 2 == 0 then
faktor1 = faktor1 / 2
xreduce = 1
end
if faktor2 % 3 == 0 then
faktor2 = faktor2 / 3
yreduce = 1
yloop = 1
end
while (0 == 0) do
feed_the_dog()
if #erg*8/faktor2 > #qrdata then
if mode == 9 then
return erg,erg1
else
if xreduce == 0 then
if yreduce == 0 then return erg,"21" end
if yreduce == 1 then return erg,"01" end
else
if yreduce == 0 then return erg,"20" end
return erg,"00"
end
end
end
local zeile = ""
local zeile1 = ""
feed_the_dog()
while (0 == 0) do
feed_the_dog()
if #zeile/faktor1 > lensym*yloop*#qrdata then
break
end
for i1=1,yloop do
local dot = {nil,nil,nil,nil,nil,nil,nil,nil}
for i = 1,8 do
local hdot = #erg*8*i1 + i
dot[i] = qrdata[math.ceil(hdot/faktor2)]
if dot[i] ~= nil then
dot[i] = dot[i][math.ceil(#zeile/lensym/faktor1)]
end
if dot[i] == nil or dot[i] < 0 then
dot[i] = 0
else
dot[i] = 1
end
end
local symbol = ( 16*(8*dot[1] + 4*dot[2] + 2*dot[3] + dot[4]) + (8*dot[5] + 4*dot[6] + 2*dot[7] + dot[8]) )
if lensym == 1 then
zeile = zeile .. string.char(symbol)
else
zeile = zeile .. "QQx" .. string.format("%02x",symbol)
end
if mode == 9 then
zeile1 = zeile1 .. tostring(dot[1]) .. tostring(dot[2]) .. tostring(dot[3]) .. tostring(dot[4]) .. tostring(dot[5]) .. tostring(dot[6]) .. tostring(dot[7]) .. tostring(dot[8])
end
end
end
table.insert(erg,zeile)
if mode == 9 then
for i=1,8 do
table.insert(erg1,"")
for j=1,#zeile1 do
erg1[#erg1] = erg1[#erg1] .. string.sub(zeile1,j*8+i,j*8+i)
end
end
end
end
end
--**********************************************************************
function App.reset_squeeze (self)
self.squeeze_out = nil
if file_open ~= nil then
start_timer(24,1,0,1)
end
end
--**********************************************************************
function App.set_lang_nr_for_report (self,actual_country0)
local actual_country = actual_country0
if actual_country0 == nil then
actual_country = self.reportlang:read()
end
self.lang_nr_for_report = 999
for i,country in ipairs(self.country_table) do
if self.country_table[i] == actual_country then
self.lang_nr_for_report = i
end
end
if self.lang_nr_for_report == 999 then
self:set_lang_nr_for_report( string.sub(self.lang_selector:read(),1,2))
end
-- print("LLL",self.lang_selector:read(),self.reportlang:read())
-- return end function xxx1 ()
if (string.sub(self.lang_selector:read(),1,2) == self.reportlang:read()) then
self.reportlang1:set_visiable(0)
self.reportlang:set_visiable(0)
else
-- print("REPORT LANGUAGE:",actual_country,self.reportlang:read())
set_text(1,200,"SN "..tostring(self.reportlang:read().." "..self.lang_selector:read().." "..tostring(actual_country)))
if actual_country0 == actual_country then
self.reportlang:write(actual_country)
end
self.reportlang1:set_visiable(1)
self.reportlang:set_visiable(1)
end
end
--**********************************************************************
function App.no_simulation (self)
local kn1 = self
local simulation = "no"
end
--**********************************************************************
function App.print_report (self)
local text = self:decrypt( pub_key3, my_read_filedata1('printbuffer.txt',sd_dir..'/printbuffer.txt') )
print("PRESS-NUMBER", self.press_protocol)
local prdata = {}
for zeile in string.gmatch(text,'[^\n]+') do
table.insert(prdata,zeile)
end
if #prdata < 2 and self.press_protocol > 1 then -- hier der Fall, in dem der Printbuffer schon leer ist.
print("WAIT-LOOP")
WAIT_26 = 10 -- Intervall, in dem auf den naechsten Block der Signatur-Berechnung
self.squeeze_out = 1 -- gewartet wird.
-- self.press_protocol = 1 -- Die aktuellen Daten des (auch ggfs halbfertigen) Reports werden gedruckt
start_timer(25,10,0,1)
else
-- if #prdata > 2 then -- hier werden die Printbuffer-Daten ausgedruckt
self.report_wird_gedruckt:set_visiable(1)
-- end
print("QQQQ","DRUCK")
self:send_to_printer(prdata,1) -- loesche den Printbuffer erst, wenn alle Ticketreports ausgedruckt sind, deswegen ist die untere Zeile in die App.on_timer Funktion gewandert
-- my_write_filedata1('printbuffer.txt',sd_dir..'/printbuffer.txt',self:crypt( sec_key3,' ',80),over_write)
-- -- der lange Leerzeichen-String ist notwendig, weil sonst die Verschluesselung nicht funktioniert!
end
end
--**********************************************************************
function App.pin_base (self,control)
if control == 12 then -- kommt vom adm_ext Screen
self.pin_offset = 0
self.pin_list = {}
self:update_list()
self:pin_screen()
return
end
if control == 99 then
self.adm_ext:activate()
-- print ('PIN_BASE', control)
elseif control == 12 then
if self.pin_offset > 0 then
-- print('PIN',12,self.pin_offset)
self.pin_offset = 0
self:pin_screen()
end
elseif control == 14 then
if self.pin_offset >= self.ANZAHL_PINS then
-- print('PIN',14,self.pin_offset)
self.pin_offset = self.pin_offset - self.ANZAHL_PINS
self:pin_screen()
end
elseif control == 7 then
if self.pin_offset + self.ANZAHL_PINS + 2 < #self.pin_list then
-- print('PIN',7,self.pin_offset)
self.pin_offset = math.min(self.pin_offset + self.ANZAHL_PINS)
self:pin_screen()
end
elseif control == 8 then
if self.pin_offset - self.ANZAHL_PINS < #self.pin_list then
-- print('PIN',8,self.pin_offset)
self.pin_offset = #self.pin_list - self.ANZAHL_PINS
self:pin_screen()
end
else
control = control % 10
if control == 0 then
control = 10
end
local pin_defs = self:read_pin_defs()
local pin_entry = self.pin_entries[control]
local pin = pin_entry[1]:read()
local mode = pin_entry[2]:read()
local remark = pin_entry[3]:read()
local entry = mode .. pin .. trim(remark)
local x1,x2 = string.find(pin_defs,',.'..pin..'.-,')
if x1 ~= nil then -- Eintrag ist vorhanden, muss jetzt geandert werden
local old_text = string.sub(pin_defs,x1,x2)
pin_defs = string.gsub(pin_defs,old_text,','..entry..',')
else -- Eintrag ist noch nicht vorhanden, muss erzeugt werden
pin_defs = pin_defs .. entry .. ','
end
self.pin_definitions:write(pin_defs..',')
self:update_list()
if mode == 'A' then
local nr_of_pin = 0
while nr_of_pin < 10000 and tostring(self.pin_list[nr_of_pin]) ~= pin do
nr_of_pin = nr_of_pin + 1
end
self.pin_offset = 10 * math.floor(nr_of_pin/10)
-- print(self.pin_offset)
end
-- self.pin_offset = 0
self:pin_screen()
end
end
--**********************************************************************
function App.read_pin_defs (self)
local pin_defs = self.pin_definitions:read()
local x1,x2 = string.find(pin_defs,'.-,,')
if x1 == nil then
pin_defs = ','
else
pin_defs = string.sub(pin_defs,x1,x2-1)
end
return(pin_defs)
end
--**********************************************************************
function App.check_if_tarif2_is_necessary (self)
local rpd = self:read_pin_defs()
if string.find(rpd,',E') then
self.is_tarif2:set_visiable(0)
end
end
--**********************************************************************
function App.pin_screen (self) -- schreibt einen PIN-Screen
local pin_defs = self:read_pin_defs()
-- print('PIN_DEFS',pin_defs)
-- self:get_pin(self.pin_offset + self.ANZAHL_PINS + 1)
local zaehler = 0
while zaehler < self.ANZAHL_PINS do
zaehler = zaehler + 1
local entry = self.pin_list[zaehler+self.pin_offset]
local mode = ' '
local remark = ''
local x1,x2 = string.find(pin_defs,',.'..tostring(entry)..'.-,')
if x1 ~= nil then
mode = string.sub(pin_defs,x1+1,x1+1)
remark = string.sub(pin_defs,x1+7,x2-1)
end
-- print('WWW',entry,mode,remark,zaehler)
self.pin_entries[zaehler][1]:write( entry )
self.pin_entries[zaehler][2]:write( mode )
self.pin_entries[zaehler][3]:write( remark )
end
self.pin_admin:activate()
end
--**********************************************************************
function App.update_list (self)
math.randomseed(tonumber(self.pin_randomseed:read()))
local new_list = {}
local zaehler = 0
local pin_defs = self:read_pin_defs()
while 0 == 0 do
nr = math.random(10009,99991)
if #self.pin_list > 0 then
table.remove(self.pin_list)
end
local x1,x2 = string.find(pin_defs,',.'..tostring(nr)..'.-,')
local sortnr = '9'
if x1 ~= nil then
zaehler = zaehler + 1
if string.sub(pin_defs,x1+1,x1+1) == 'A' then
sortnr = '1'
end
pin_defs = string.gsub(pin_defs,',.'..tostring(nr)..'.-,',',')
end
table.insert(new_list,{sortnr..tostring(nr),nr})
if #pin_defs < 4 and #self.pin_list == 0 then
if #new_list % self.ANZAHL_PINS == 0 and #new_list > 1.3*zaehler then
break
end
end
end
table.sort(new_list,function (a,b) return(a[1]<b[1]) end)
for i,nr in ipairs(new_list) do
table.insert( self.pin_list, tonumber(nr[2]) )
end
end
--**********************************************************************
function App.pin_list_change (self) -- Aendert alle PINs mittels einer neuen Random-Seed
math.randomseed(tonumber(self:time())*1000+tonumber(get_timer_value(29)))
self.pin_randomseed:write( tostring(math.random(1030507,9030507)) )
print("----------------------")
-- print("RS1",self.pin_start:read())
local rs = fcheck(0,tonumber,self.pin_start:read())
-- print("RS2",rs)
if rs == nil then
rs = 0
end
if 1000 <= rs and rs <= 9999999 then
self.pin_randomseed:write( tostring(rs) )
end
math.randomseed(tonumber(self.pin_randomseed:read()))
local pin_defs = string.sub( self:read_pin_defs(), 2 )
local pins_new = ','
while #pin_defs > 2 do
local x1,x2 = string.find(pin_defs,'.-,')
pins_new = pins_new .. string.sub(pin_defs,1,1) .. math.random(10009,99991) .. string.sub(pin_defs,7,x2-1) .. ','
-- print(pins_new)
pin_defs = string.sub(pin_defs,x2+1)
end
self.pin_definitions:write(pins_new..',')
local pin_defs1 = self:read_pin_defs()
-- print('PP',pin_defs1)
self.pin_list = {}
end
--**********************************************************************
function App.activate_ticketslot (self)
local tan = self.ticketslot_tan1:read() .. self.ticketslot_tan2:read() .. self.ticketslot_tan3:read()
-- set_text(14,11,tostring(self.flash_is_not_signed) .. ' ' .. tostring(self.report_is_integer))
self.no_timeout = 0
local rid = self.request_id
self.request_id = 0
self:renew_timeout()
-- print('SR',rid)
if self.flash_is_not_signed == 1 and rid == 0 then -- falls jemand einfach beim Speicher signieren
self:disable_device() -- auf OK drueckt, ohne vorher eine TAN angefordert zu haben.
return -- Denn nur der Fall, dass die Daten auf der SD-Karte falsch signiert
end -- sind, soll toleriert werden
if rid > 86000 then
if self:check_tan(0, tostring(rid) ,tan) == 1 then
if rid > 93000 then
self:check_flash_signature('write new signature')
self.flash_is_not_signed = 0
end
self.normal_test1 = "1436"
local reports = self:read_report()
self.report_is_integer = 1
error2("wr80")
self:write_report(reports)
error2("wr81")
self:update_counter(0)
set_backlight(self.BLACKSCREEN)
self.standby:activate()
end
if self.flash_is_not_signed == 1 then
print('not ready')
self:disable_device ()
return
end
-- print('FR',self.flash_is_not_signed,self.report_is_integer)
if self.report_is_integer == 1 then
return
end
end
if self.report_is_integer == 0 then
self.report_is_integer = 2 -- 2 weil wir mit nicht signiertem reportfile arbeiten
self:update_counter(0)
set_backlight(self.BLACKSCREEN)
self.standby:activate()
return
end
local stored_pins = self.ticketslot_pin:read()
local pin1 = ''
self.no_timeout = 1
self.admin:activate()
while #stored_pins > 4 do
if file_open ~= nil then
feed_dog()
end
self:renew_timeout()
local pin = string.sub(stored_pins,1,5)
-- print(pin)
stored_pins = string.sub(stored_pins,6)
if self:check_tan(0, pin, tan) == 1 then
local ri = self.report_is_integer
self.normal_test1 = "1437"
local reports = self:read_report()
self.ticketslot_pin:write(pin1 .. stored_pins)
self:update_counter(300000)
self.report_is_integer = ri
error2("wr83")
self:write_report(reports,self:date("%y%m%d%H%M%S").."X","0,yy")
error2("wr84")
return
else
pin1 = pin1 .. pin
end
end
end
--**********************************************************************
function App.disable_device (self,no_screen_change)
for i = 0, 31, 1 do
stop_timer(i)
end
if no_screen_change == nil then
set_backlight(100)
lang_nr = self.lang_nr_for_report
self.not_ready:activate("force")
end
self.no_timeout = 1
self.uart_sperre = 1
self.last_knob_action_was_press = 0
DISABLED = 1
end
--**********************************************************************
function App.heartbeat_entry (self)
if self.ticket_wird_gerade_gedruckt == 0 then
local new_print_date = self:date("%y%m%d%H%M%S")
if string.sub(new_print_date,1,10) ~= string.sub(self.last_print_date,1,10) then -- man braucht nur jede Stunde einen Eintrag
self.normal_test1 = "1438"
local reports = self:read_report()
if self.ticket_wird_gerade_gedruckt == 0 then
error2("wr85")
self:write_report(reports,new_print_date..".","0,00")
error2("wr86")
self.last_print_date = new_print_date
end
end
end
end
--**********************************************************************
function App.on_timer (self,tid)
-- print('Timer call'..tostring(tid))
if tid == 0 then
-- self.uart_sperre = 0
self.normal_test1 = "1439"
local reports = self:read_report() -- die Daten in das Ticketreporting reinschreiben
local betr = self.anzeige_betrag:read()
if trim(betr) ~= '0,00' and self.real_coins == 1 then
error2("wr87")
self:write_report(reports,self:date("%y%m%d%H%M%S")..CURRENTSHORT,betr)
error2("wr90")
-- self:write_report(reports,self:date("%y%m%d%H%M%S").."A",betr)
self:reset_coin()
-- self.anzeige_betrag:write('0,00')
end
self.no_timeout = 0
set_backlight(self.BLACKSCREEN)
self.standby:activate()
elseif tid == 1 and self.no_timeout == 0 then -- @@
-- self.uart_sperre = 0
self.pin_feld:write('')
-- self.sondertarif_anzeige:set_visiable(1)
-- self.tarifnr = 1
-- self.anzeige_betrag:write('0,00')
self.ungueltig = 0
self:switch_language(1)
set_backlight(self.BLACKSCREEN)
self.standby:activate()
elseif tid == 2 then
self:reset_coin()
self.admin_count = 0
-- self.uart_sperre = 0
if self:read_counter() == 0 or self.there_is_no_sdcard == 1 then
print("READ COUNTER is 0")
self:disable_device()
return
else
self.no_timeout = 0
self:renew_timeout()
self:switch_language(1)
self:choose_flags()
self.lang:activate()
end
elseif tid == 3 and self.no_timeout == 0 then
-- self.uart_sperre = 0
if file_open == nil then
print('LOW BACKLIGHT')
end
set_backlight(self.LOW_BACKLIGHT)
self.ueberdeckung:set_visiable(1)
if file_open == nil then
self.show_low:set_visiable(1)
end
elseif tid == 4 then
self.admin_count = 0
self.control0 = nil
elseif tid == 5 then
-- self.uart_sperre = 0
local text = trim(self.pin_feld:read())
self.pin_feld:write( string.sub(text,1,#text-1) )
self:renew_timeout()
if #text == 1 then
self.pin_wartezeit = 0
self.ungueltige_pin:set_visiable(1)
else
start_timer(5,self.pin_wartezeit,0,1)
end
elseif tid == 6 then
-- uart_send_data( self:str_to_byteArray(' '))
-- uart_send_data( self:str_to_byteArray(' '))
-- uart_send_data( self:str_to_byteArray(' '))
-- elseif tid == 7 then
-- self:uarthandler()
elseif tid == 7 then
self:print_parkticket()
-- self.uart_sperre = 0
elseif tid == 8 then
self:on_control_notify1(ACT_SCREEN,ACT_TASTE) -- fuer Tests
self:test1_simu_taste()
elseif tid == 9 then
-- table.insert(self.uartbuffer,0)
self.long_press = 1
-- self.catch_release = 1
self:on_uart_recv_data( { [0] = 0xEE, [1] = 0xB1, [2] = 22 } )
-- self:on_control_notify1( self:current_screen().screen_id , 80)
elseif tid == 14 then
feed_the_dog()
start_timer(14,4000,0,1)
elseif tid == 15 then
self.zeit_umstellen()
elseif tid == 16 then
self.uart_sperre = 0
self:print_testausdruck()
elseif tid == 17 then
self:reset_coin()
self:renew_timeout()
elseif tid == 18 then -- ruecksetzen nach mehrfacher (7) falscher QR-Code Eingabe
self.no_timeout = 0
print("QRR",self.qrzaehler)
if self.qrzaehler > 5 then
self:on_timer(2)
else
self.coin:activate()
end
self:renew_timeout()
if not (self.start_test_nr == "") then
self:start_test("")
end
elseif tid == 19 then
self.qr_invalid:set_visiable(-1)
self.qrtan:write("")
elseif tid == 20 then -- Testprogramme, laufen nach 5 Sekunden, dazu muss im QR-Eingabe Modus (virtuelle Muenzen)
self.nochmal:set_visiable(-1)
self.nochmal_button:set_visiable(-1)
self.lang:activate()
self:start_test(self.start_test_nr) -- eine fuenfstellige Nummer, die mit 0 beginnt, eingegeben werden
elseif tid == 21 then
self:on_uart_recv_data( { [0] = 0xEE, [1] = 0xB1, [2] = 21 } )
elseif tid == 22 then
self:on_uart_recv_data( { [0] = 0xEE, [1] = 0xB1, [2] = 22 } )
elseif tid == 23 then
feed_the_dog()
local drucke_zeilen_jetzt = 1
local zeilen_jetzt = {}
local zeilen_spaeter = {}
local pausen_interval = 10
for i,zeile in ipairs( self.print_data ) do
if drucke_zeilen_jetzt == 0 then
table.insert(zeilen_spaeter,zeile)
elseif string.sub(zeile,1,5) == "PAUSE" then
drucke_zeilen_jetzt = 0
feed_the_dog()
pausen_interval = tonumber(string.sub(zeile,6,99))
else
table.insert(zeilen_jetzt,zeile)
end
end
self.print_data = zeilen_spaeter
-- print("ANZZ",#self.print_data)
feed_the_dog()
self:send_to_printer1( zeilen_jetzt )
feed_the_dog()
if #self.print_data == 0 then
start_timer(24,50,0,1) -- keine Daten mehr da, Druck beenden
else
start_timer(23,pausen_interval,0,1) -- um fuer den Schnitt des Papiers Zeit zu lassen
end
elseif tid == 24 then
print("VISIABLE OFF")
self.report_wird_gedruckt:set_visiable(0)
if self.delete_printbuffer == 1 then
error2("newbuf01")
my_write_filedata1('printbuffer.txt',sd_dir..'/printbuffer.txt',self:crypt( sec_key3,' ',80),over_write)
error2("newbuf02")
-- der lange Leerzeichen-String ist notwendig, weil sonst die Verschluesselung nicht funktioniert!
end
elseif tid == 25 then
self.debug1:write("Start to generate report with thread")
self.report_data = {}
function thr1 () self:generate_report() end
xzaehl = 0
if file_open == nil then
WAIT_26 = 10
end
self.generate_report_thread = coroutine.create( thr1 )
coroutine.resume(self.generate_report_thread)
elseif tid == 26 then
-- self:renew_timeout() @@ aktivieren fuer test
xzaehl = xzaehl + 1
self.debug1:write("Resume ROUND "..tostring(xzaehl))
coroutine.resume(self.generate_report_thread)
elseif tid == 27 then -- Timer-Pufferung des ggfs. recht langen Intervalls bis zur naechsten Zeitumstellung
self.zeitumstellungs_zaehler = self.zeitumstellungs_zaehler + 1
if self.zeitumstellungs_zaehler > 200 then
self:zeit_umstellen()
else
start_timer(27,self.zeitumstellungs_intervall,0,1)
end
elseif tid == 28 then
self:insert_coin(self.nr_coin)
elseif tid == 29 then -- heartbeat mit Eintraegen in die newreport.txt
self:heartbeat_entry()
print("heartbeat")
start_timer(29,3300000,0,1) -- alle 55 Minuten einen Heartbeat versuchen
end
end
--**********************************************************************
function App.xxon_uart_recv_data(self,packet,real_knob)
-- print("UART_SPERRE",uart_sperre)
-- if self.uart_sperre == 1 then
-- return
-- end
if HAS_KNOB ~= 1 and real_knob ~= nil then
local bed = HAS_KNOB == 0
HAS_KNOB = 1
self:coins_and_knob(bed)
end
if packet[0]==0xEE and packet[1]==0xB1 then
if packet[2]==0x10 then
if packet[3] == 0x01 then coin_nr = 1081
elseif packet[3] == 0x02 then coin_nr = 1082
elseif packet[3] == 0x05 then coin_nr = 1083
elseif packet[3] == 0x0a then coin_nr = 1084
elseif packet[3] == 0x14 then coin_nr = 1085
end
self:on_control_notify1( self:current_screen() ,coin_nr)
elseif packet[2] == 21 then
self:press_action()
elseif packet[2] == 22 then
self:release_action()
end
end
end
--**********************************************************************
function App.on_uart_recv_data(self,packet,real_knob)
if self.uart_sperre == 1 then
return
end
if HAS_KNOB == 0 and real_knob ~= nil then -- oh, es ist ja DOCH ein Knopf da!
HAS_KNOB = 1
self.knob1:set_visiable(-1)
end
local screen = self:current_screen()
if screen == nil then
screen = self.standby
end
-- print("GCS",get_current_screen())
-- local pr_re = 1
-- print("SID",screen,self.standby)
-- if screen == self.standby then
-- pr_re = 2
-- end
local pr_re = self.press_release
-- print("PR_RE",pr_re,screen)
if packet[0]==0xEE and packet[1]==0xB1 then
if packet[2]==0x10 then
if packet[3] == 0x01 then coin_nr = 1081
elseif packet[3] == 0x02 then coin_nr = 1082
elseif packet[3] == 0x05 then coin_nr = 1083
elseif packet[3] == 0x0a then coin_nr = 1084
elseif packet[3] == 0x14 then coin_nr = 1085
elseif packet[3] == 0x18 then coin_nr = 1086
elseif packet[3] == 0x22 then coin_nr = 1089
end
self:on_control_notify1( self:current_screen() ,coin_nr)
elseif packet[2] == 21 then -- press knob
-- print("PP",21,pr_re)
-- table.insert(self.uartbuffer,1)
-- if self.uart_sperre == 1 then
-- return
-- end
self.last_knob_action_was_press = 1
self.long_press = 0
-- self.long_was_pressed = 0
if pr_re > 0 then
stop_timer(9)
self.akt_screen = screen
start_timer(9,1500,0,1)
end
-- set_text(1,21,"PRESS")
-- set_text(1,22,"--")
-- set_backlight(30)
-- print(screen)
if pr_re < 2 then
self:on_control_notify1( screen.screen_id , 80)
end
elseif packet[2] == 22 then -- release knob
if pr_re > 0 then
stop_timer(9)
end
-- set_text(1,22,"RELEASE")
-- set_text(1,21,"--")
-- print("LWP",self.last_knob_action_was_press)
if self.last_knob_action_was_press == 1 and (self.long_press == 1 or pr_re == 2) then
self:on_control_notify1( screen.screen_id , 80)
-- else
-- if self.long_was_pressed == 1 then
-- self:on_control_notify1( self:current_screen().screen_sid , 80)
-- end
end
self.last_knob_action_was_press = 0
end
end
end
--**********************************************************************
function App.press_action (self)
-- print(' PP',21)
-- table.insert(self.uartbuffer,1)
local pr_re, screen = self:get_pr_re()
self.last_knob_action_was_press = 1
self.long_press = 0
self.long_was_pressed = 0
if pr_re > 0 then
stop_timer(9)
self.akt_screen = screen
start_timer(9,1500,0,1)
end
set_text(1,21,'PRESS')
set_text(1,22,'--')
set_backlight(self.LOW_BACKLIGHT)
-- print(screen)
if pr_re < 2 then
self:on_control_notify1( screen.screen_id , 80)
end
end
--**********************************************************************
function App.release_action (self)
-- print(' PP',22)
-- table.insert(self.uartbuffer,0)
local pr_re, screen = self:get_pr_re()
if pr_re > 0 then
stop_timer(9)
end
set_text(1,22,'RELEASE')
set_text(1,21,'--')
-- print('LWP',self.last_knob_action_was_press)
if self.last_knob_action_was_press == 1 and (self.long_press == 1 or pr_re == 2) then
self:on_control_notify1( screen.screen_id , 80)
-- else
-- if self.long_was_pressed == 1 then
-- self:on_control_notify1( self:current_screen().screen_sid , 80)
-- end
end
self.last_knob_action_was_press = 0
end
--**********************************************************************
function App.get_pr_re (self)
local screen = self:current_screen()
if screen == nil then
screen = self.standby
end
-- print('GCS',get_current_screen())
local pr_re = 1
-- print('SID',screen,self.standby)
if screen == self.standby then
pr_re = 2
end
-- print('PR_RE',pr_re)
return pr_re, screen
end
--**********************************************************************
function App.on_uart_recv_data1(self,packet) -- UART Test
if packet[0]==0xEE and packet[1]==0xB1 then
if packet[2]==0x10 then
if packet[3] == 0x01 then coin_nr = 1081
elseif packet[3] == 0x02 then coin_nr = 1082
elseif packet[3] == 0x05 then coin_nr = 1083
elseif packet[3] == 0x0b then coin_nr = 1084
elseif packet[3] == 0x14 then coin_nr = 1085
end
elseif packet[2] == 21 then
set_text(1,21,'PRESS')
set_text(1,22,'--')
elseif packet[2] == 22 then
set_text(1,22,'RELEASE')
set_text(1,21,'--')
end
end
end
--**********************************************************************
function App.uarthandler (self) -- fruehere Version, indirekte Aufrufe
local pr_re = self.press_release
pr_re = 1
local knob_event = 0
while 0 == 0 do
self.uartzaehler = self.uartzaehler + 1
knob_event = self.uartbuffer[ self.uartzaehler ]
if knob_event == nil then
self.uartzaehler = self.uartzaehler - 1
break
end
if knob_event == 1 then
self.long_press = 0
self.last_knob_action_was_press = 1
if pr_re < 2 then
self:on_control_notify1( self:current_screen().screen_sid , 80)
end
else
if self.last_knob_action_was_press == 1 then
self.last_knob_action_was_press = 0
if self.long_press == 1 and pr_re == 1 or pr_re == 2 then
self:on_control_notify1( self:current_screen().screen_sid , 80)
end
else
if self.long_press == 0 then
self:on_control_notify1( self:current_screen().screen_sid , 80)
end
end
end
end
start_timer(7,30,0,1)
end
--**********************************************************************
function App.xxuarthandler (self)
local knob_event = 0
while 0 == 0 do
if #self.uartbuffer == 0 then
break
end
knob_event = table.remove(self.uartbuffer,1)
if knob_event == 1 then
self.long_press = 0
self.last_knob_action_was_press = 1
self:on_control_notify1( self:current_screen().screen_sid , 80)
elseif knob_event == 0 or knob_event == 9 then
if knob_event == 0 and self.last_knob_action_was_press == 1 and self.long_press == 1 then
self:on_control_notify1( self:current_screen().screen_sid , 80)
end
self.last_knob_action_was_press = 0
end
end
start_timer(7,30,0,1)
end
--**********************************************************************
function App.xxcheck_for_event (self,scr)
-- print('check for event')
if self.last_press_button ~= self.press_button:read() then
-- if self.ticket_wird_gerade_gedruckt == 0 then
self:on_control_notify1(scr,80)
-- end
self.press_button:write('released')
self.last_press_button = 'released'
end
if self.last_pluse_sum ~= self.pluse_sum:read() then
local coin_diff = tonumber(self.pluse_sum:read()) - tonumber(self.last_pluse_sum)
self.last_pluse_sum = self.pluse_sum:read()
if coin_diff > 0 then
if coin_diff == 1 then self:on_control_notify(scr,1081)
elseif coin_diff == 2 then self:on_control_notify(scr,1082)
elseif coin_diff == 5 then self:on_control_notify(scr,1083)
elseif coin_diff == 10 then self:on_control_notify(scr,1084)
elseif coin_diff == 20 then self:on_control_notify(scr,1085)
else print('coin evaluation error') end
end
end
end
--**********************************************************************
function App.renew_timeout(self)
set_backlight(100)
self.ueberdeckung:set_visiable(-1)
self.show_low:set_visiable(-1)
-- self.nochmal:set_visiable(-1)
stop_timer(0)
stop_timer(1)
stop_timer(3)
stop_timer(18)
if self.no_timeout == 0 then
start_timer(0,self.TIMEOUTLONG*1000,0,1)
start_timer(1,self.TIMEOUT*1000,0,1)
start_timer(3,self.REDUCE_BACKLIGHT*1000,0,1)
end
end
--**********************************************************************
function App.renew_qr_timeout(self)
set_backlight(100)
self.ueberdeckung:set_visiable(-1)
self.show_low:set_visiable(-1)
-- self.nochmal:set_visiable(-1)
stop_timer(0)
stop_timer(1)
stop_timer(3)
stop_timer(18)
start_timer(18,self.TIMEOUTQRCODE*1000,0,1)
start_timer(3,self.QRCODE_REDUCE*1000,0,1)
end
--**********************************************************************
function App.start_test (self,nr)
if SERIALNUMBER ~= "9999999" then
return
end
if nr == "" then
self:on_timer(20)
return
end
print("Test No.",nr)
if nr == "03301" then
self:test1_generate_report("xx")
end
if nr == "03302" or nr == "03303" then
self:test1_print_big_qrcode("xx",nr)
end
if nr == "03305" or nr == "03306" then
self.simu_par = nr
self.tastenfolge = {101}
self:test1_simu_taste()
end
end
--**********************************************************************
--private
Screen = {}
Screen.__index = Screen
--**********************************************************************
function Screen:new (app,screen_id,multilang)
local self = setmetatable({},Screen)
self.app = app
self.screen_id = screen_id
self.act_lang = -1 -- language is undefined
if multilang == nil then
self.multilang_fields = {}
else
self.multilang_fields = multilang
end
self.app.screens[tonumber(screen_id)] = self
self.pr_re = self.app.press_release
return self
end
--**********************************************************************
function Screen.get_text (self,nr)
if self.app.simulation == 1 then
text = self.app.field_sim[tostring(self.screen_id)..'.'..tostring(nr)]
if text == nil then
text = ''
end
elseif nr > 0 then
text = get_text(self.screen_id,nr)
-- else
-- local erg = get_value(self.screen_id,-nr)
-- erg = tonumber(erg)
-- if erg < 0.5 then
-- text = '0'
-- else
-- text = '1'
-- end
end
return text
end
--**********************************************************************
function Screen.set_text (self,nr,text)
if self.app.simulation == 1 then
self.app.field_sim[tostring(self.screen_id)..'.'..tostring(nr)] = text
elseif nr > 0 then
local ggg = self.screen_id
-- ZF1 = tostring(math.random(1001,99942))
set_text(ggg,nr,text)
-- ZUFALLSWERT = ZF1 .. " " .. tostring(math.random(1001,99942)) .. " " .. tostring(self.screen_id) .. " " .. tostring(nr)
-- .. " " .. text
-- else
-- set_value(self.screen_id,-nr,text)
end
end
--**********************************************************************
function Screen.activate (self,force)
-- self.screen_id = self.app.no_sdcard.screen_id
-- return("")
self.screen_id = self.app.no_sdcard.screen_id set_backlight(100) -- SWITCH main0.lua
if self.act_lang ~= lang_nr then
force = "force"
end
if force == nil then
if self.app:current_screen() == self then
return
end
else
self.act_lang = -1
end
if self.act_lang ~= lang_nr then
for i,nr in ipairs(self.multilang_fields) do
local text = self:get_text(nr)
if text == "-" or text == "" or string.find(text,"+") or string.find(text,"%%")
or string.find(text,"?") or string.find(text,"'") then
text = "S" .. tostring(self.screen_id) .. "F" .. tostring(nr)
end
-- print(nr,text,t(text))
self:set_text(nr,t(text))
end
end
if self.app.simulation == 0 then
if self.app.screen_switch_is_blocked == 0 then
change_screen(self.screen_id)
ACT_SCREEN = self.screen_id
end
end
self.act_lang = lang_nr
self.app.press_release = self.pr_re
end
--**********************************************************************
--private
Field = {}
Field.__index = Field
--**********************************************************************
function Field:new (app,default_text,screen,control_nr)
local self = setmetatable({},Field)
self.app = app
if default_text == nil then
default_text = ''
end
self.default_text = default_text
self.screens = {}
self.flash_position = nil
if screen ~= nil then
self:add_field(screen,control_nr)
end
if tonumber(self.app.use_default_values) == 1 then
self:write(default_text,'no signature')
end
return self
end
--**********************************************************************
function Field.add_field (self,screen,control_nr)
table.insert(self.screens,{screen,control_nr})
-- print(screen,control_nr,self.default_text)
if self.app.simulation == 1 then
screen:set_text(control_nr,self.default_text)
end
end
--**********************************************************************
function Field.add_flash_position (self,offset,laenge)
self.flash_position = offset
self.flash_length = laenge
table.insert(self.app.fields,self)
if self.simulation == 0 then
self:read() -- for immediate synchronizing screen fields and flash
end
end
--**********************************************************************
function Field.read (self)
if self.zwischenspeicher ~= nil then
return(self.zwischenspeicher)
end
local erg = nil
for i,control in ipairs(self.screens) do
local screen = control[1]
local control_nr = control[2]
local erg1 = screen:get_text(control_nr)
if erg == nil then
erg = erg1
elseif erg ~= erg1 then
erg = nil -- die Screens stimmen nicht ueberein
break
end
end
-- print('EE',erg)
local flash_store = nil -- Flash Store auch auslesen
if self.flash_position ~= nil then
if self.app.simulation == 0 then
flash_store = read_flash_string(self.flash_position)
end
if flash_store == nil then
-- print("READ",self.flash_position,flash_store)
if self.app.simulation == 0 then
write_flash_string(self.flash_position,self.default_text)
end
flash_store = self.default_text
end
if erg ~= flash_store then
self:update_screens(flash_store)
erg = flash_store
end
else
if erg == nil then
self:update_screens(self.default_text)
erg = self.default_text
end
end
return(erg)
end
--**********************************************************************
function Field.write (self,text)
self:update_screens(text)
if self.flash_position ~= nil then
self.zwischenspeicher = text
self.app:check_flash_signature('update')
self.zwischenspeicher = nil
write_flash_string(self.flash_position,text)
end
end
--**********************************************************************
function Field.update_screens (self,flash_store)
for i,control in ipairs(self.screens) do
local screen = control[1]
local control_nr = control[2]
if control_nr == nil then
error1("update_screens")
else
screen:set_text(control_nr,flash_store)
end
end
end
--**********************************************************************
function Field.set_visiable (self,is_visiable)
for i,sf in ipairs(self.screens) do
-- print('XX',sf[1].screen_id,sf[2],is_visiable)
if self.app.simulation == 0 then
set_visiable(sf[1].screen_id, sf[2], math.max(0,is_visiable) )
if is_visiable < 0 then
set_enable(sf[1].screen_id, sf[2], 0 )
else
set_enable(sf[1].screen_id, sf[2], 1 )
end
end
end
end
--**********************************************************************
function Field.synchronize_with_entry (self,screen,control)
for i,scr in ipairs(self.screens) do
-- print(screen.screen_id,control,scr[1].screen_id,scr[2])
if screen == scr[1] and control == scr[2] then
local val = screen:get_text(scr[2])
-- print(screen,control,val)
-- print('QQ',control,val)
self:write(val)
screen:activate()
return 1
end
end
return 0
end
--**********************************************************************
function xxcheck_initialization_string (nr,init_string)
-- Wenn nr1 == '', dann wird eine Zufallsnummer ausgegeben
-- Wenn init_string == '', dann wird der zu nr zugehoerige init:string ausgegeben
-- Ansonsten wird eine Pruefung gegen den init_string gemacht (Ergebnis 0 oder 1)
local nr1 = 0
if nr == '' then
math.randomseed(os.time())
nr1 = tostring(math.random(11111,99999))
else
nr1 = tostring(nr)
end
if nr == '' then
return nr1
end
local p_a = tonumber(string.sub(nr1,1,2)) + 2
local p_b = tonumber(string.sub(nr1,3,4)) + 5
local p_c = tonumber(string.sub(nr1,5)) + 1
local sk_bytes = bin.hextobin(string.sub(sec_key,1,64) .. rk)
local text = {} -- {[1] = '0'}
while #text < 24 do
local x = #text
local y = (p_a + p_b * x + p_c * x * x) % #sk_bytes + 1
table.insert(text,sk_bytes[y])
end
text = bin.bintohex(text)
text = bin.hextos(text)
text = base27_encode(text)
text = string.sub(text..'XX',1,5)
if init_string == '' then
return(text)
elseif init_string == text then
return(1)
else
return(0)
end
end
--**********************************************************************
function xxupdate_screen_data_from_flash ()
local text_gesamt = ''
for k,v in pairs(flash_data) do
local text1 = trim(read_flash_string(v[1],v[2]))
set_text(v[3],v[4], text1 )
text_gesamt = text_gesamt .. text1
end
end
--**********************************************************************
function xxsign_flash ()
local text = md5_flash()
-- print('MD5_new',text)
local ciph = crypto_crypt(sec_key,text)
-- print ('CIPH_new',ciph)
local text1 = crypto_decrypt(pub_key,ciph)
-- print('VVV',text1)
write_flash_string(posmax+10,64, ciph )
end
--**********************************************************************
function xxmd5_flash ()
local contents = {}
for i,v in pairs(flash_data) do
-- print(v[1])
-- print(read_flash_string(v[1],v[2]))
table.insert(contents,{ v[1], trim(read_flash_string(v[1],v[2])) } )
end
table.sort(contents,function (a,b) return(a[1]<b[1]) end)
local text = 'x'
for i,text1 in ipairs(contents) do
text = text .. text1[2]
end
text = bin.stohex( md5lib.hash(text) )
return text
end
--**********************************************************************
function xxread_counter ()
local zaehler = 0
local adr = 50000
counter = 0
local c_min = 999999
local nrmin = 0
nrmax = 0
posmax = 0
posmin = 0
while zaehler < 10 do
pos = adr + zaehler*4096
counter1 = tonumber(read_flash_string(pos,10))
if counter1 ~= nil and counter1 > counter then
counter = counter1
nrmax = zaehler
posmax = pos
end
if counter1 ~= nil and counter1 < c_min then
c_min = counter1
nrmin = zaehler
posmin = pos
end
zaehler = zaehler + 1
end
end
--**********************************************************************
function xxcrypto_make_pin (setrandom)
-- erstmal eine Zufallszahl generieren, die dann als zu uebertragender Plaintext gilt
local plainnumber_bin = {}
local plainnumber = 0
if setrandom == 1 then
local f = file_open('/proc/self/stat','r')
math.randomseed(os.time()+os.clock()+tonumber(string.sub(f:read(),1,4)))
f.close()
end
while #plainnumber_bin < 29 do
local x = math.random(0,1)
table.insert(plainnumber_bin,tostring(x))
plainnumber = 2*plainnumber + x -- das wird die plainnumber in dezimaler Form
end
-- print('P1',table.concat(plainnumber_bin))
-- die plainnumber jetzt verschluesseln
plainnumber = tostring(math.floor(plainnumber + 110000000.1))
-- print('P1',plainnumber)
local ciphernumber = crypto_crypt(sec_key,plainnumber)
-- print('P3',bin.stohex(ciphernumber))
-- print(plainnumber)
-- print(bin.stohex(ciphernumber))
-- aus der ciphernumber jedes 9te Bits herausholen
local cipher_bits = crypto_pick_bits(ciphernumber)
-- print('CC',table.concat(cipher_bits))
-- jetzt plainnumber im binaeren format und die stichprobenartig gewaehlten Bits mischen:
local text = {}
local zaehler = 0
while zaehler < 29 do
zaehler = zaehler + 1
table.insert(text,plainnumber_bin[zaehler])
local x = cipher_bits[zaehler]
-- if x ~= nil then
table.insert(text,x)
-- end
end
-- print(table.concat(text))
-- Wir haben jetzt 48 Bit in text. Diese machen wir jetzt zu einem String mit 6 Byte,
-- den wir erst noch mal RC4-verschluesseln:
text = bin.bintohex(text)
text = bin.hextos(text)
rk1 = bin.hextos(rk)
text = rc4.encrypt(rk1,text)
-- print('QQQ',bin.stohex(text))
-- Diese verschluesselten 6 Byte machen wir zu 10 Zeichen im base27 Alphabet:
local pin = base27_encode(text)
-- Wegen 27^10 = ungefaehr 0.75 * 2^48 kommt es in 26,2 Prozent der Faelle vor, dass wir
-- 11 Zeichen kriegen in pin. Das wollen wir aber nicht, daher nochmal rechnen fuer eine PIN
--- mit 10 Zeichen:
if #pin > 10 then -- Diese Rekursion kommt zu 99,5 Prozent nach 4 Runden zum Ende
print(' repeat ...')
return crypto_make_pin(0) -- zu 99,9998 nach 10 Runden. Und braucht ausserdem wohl nur
else -- im Desktop laufen, nicht im Geraet. Deswegen kein Problem.
return pin
end
end
--**********************************************************************
function xxcrypto_check_pin (pin)
-- Die PIN ist Base27 und RC4 verschluesselt, also erstmal entschluesseln,
-- dann zu einem Bitarray umwandeln
pin = base27_decode(pin)
rk1 = bin.hextos(rk)
pin = rc4.decrypt(rk1,pin)
pin = bin.stohex(pin)
pin = bin.hextobin(pin)
local plainnumber = 0
-- local plainnumber_bin = ''
local ciphertext_part = {}
for i,v in ipairs(pin) do
if i % 2 == 1 or i > 38 then -- in diesen Bits ist die zu verschluesselnde Zahl
plainnumber = 2*plainnumber + tonumber(v)
-- plainnumber_bin = plainnumber_bin .. v
else -- in diesen Bits ist eine Auswahl der verschluesselten Zahl
table.insert(ciphertext_part,v)
end
end
-- print('P2',plainnumber_bin)
-- plainnumber ist die urspuengliche Plaintext Zahl
local plainnumber1 = tostring(math.floor(plainnumber + 110000000.1))
-- diese wird jetzt mit dem secret key verschluesselt
local ciphernumber = crypto_crypt(sec_key,plainnumber1)
-- print('P3',bin.stohex(ciphernumber))
-- alle Bits aus ciphertext_part sollten uebereinstimmen, pruefe das:
if table.concat(crypto_pick_bits(ciphernumber)) == table.concat(ciphertext_part) then
return plainnumber
else
return 0
end
end
--**********************************************************************
function xxcrypto_pick_bits (ciphertext)
ciphertext = bin.stohex(ciphertext)
ciphertext = bin.hextobin(ciphertext)
ciphertext_part = {}
for i,v in ipairs(ciphertext) do
if (i+2) % 9 == 0 and #ciphertext_part < 19 then
table.insert(ciphertext_part,v)
end
end
return ciphertext_part
end
--**********************************************************************
-- MAIN_SECTION_END
--**********************************************************************
-- CONSOLE_AREA_BEGIN
--**********************************************************************
--private
if arg ~= nil and arg[1] == 'patch' then
local parts = 25
local patch1 = io.open(arg[2])
local gesamt = 0
while 0 == 0 do
local line1 = patch1:read()
gesamt = gesamt + 1
if line1 == nil then
patch1:close()
break
end
end
patch1 = io.open(arg[2])
local zaehler = 0
local total = 0
text = {}
local chunk = ''
while 0 == 0 do
local line1 = patch1:read()
zaehler = zaehler + 1
if line1 ~= nil then
chunk = chunk .. line1 .. "\n"
end
if zaehler > math.max(10,gesamt/parts) or line1 == nil then
total = total + 1
zaehler = 0
-- print(chunk)
if #chunk < 100 then
chunk = chunk .. "\n\n--*************************************************************" ..
"*****************************************************\n\n"
end
local ciph = trim(App.crypt('',pub_key1,chunk))
-- print("----->\n") print(App.decrypt('',sec_key1,ciph))
while 0 == 0 do
table.insert(text,string.sub(ciph,1,80))
ciph = string.sub(ciph,81)
if #ciph == 0 then
break
end
end
table.insert(text,'--')
chunk = ''
end
if line1 == nil then
patch1:close()
break
end
end
if arg[3] == nil then
arg3 = ""
else
arg3 = arg[3] .. "_"
end
local fh = io.open('patch_'..arg3..SERIALNUMBER..'.enc','w')
if fh ~= nil then
fh:write("--"..tonumber(total).."\n")
for i,zeile in ipairs(text) do
fh:write(zeile.."\n")
end
end
fh:close()
local text1 = SERIALNUMBER .. '/301000/0'
local text101 = [[
20240715060632. 0,00
20240715070134. 0,00
20240715074403Q 0,ph
20240715083541Q 0,te
20240715084115Q 0,te
20240715084942Q 0,em
20240715085049Q 0,ph
20240715091341E 1,00
20240715091942E 1,00
20240715092710Q 0,bo
20240715094148E 2,00
20240715094559E 0,50
20240715100042E 1,00
20240715100239E 1,00
20240715100442E 0,50
20240715101139E 2,00
20240715101334. 0,00
20240715102222E 1,00
20240715103538E 0,50
20240715110756E 1,50
20240715110926E 2,00
20240715112242E 0,50
20240715112404Q 0,as
20240715115609E 1,00
20240715125111. 0,00
20240715134613. 0,00
20240715141719E 0,50
20240715142234E 0,50
20240715144859E 2,00
20240716105837. 0,00
20240716111512E 0,60
20240717105021. 0,00
20240717114523. 0,00
20240717124025. 0,00
20240717124940E 1,00
20240719111544. 0,00
20240719111926Q 0,em
20240719112125. 0,00
20240719114343E 0,50
20240721111930. 0,00
20240721112725E 0,50
20240722093113. 0,00
20240722100810E 2,00
20240723110222. 0,00
20240723112026E 2,00
20240724115700. 0,00
20240724124026Q 0,as
20240725114015. 0,00
20240725123517. 0,00
20240726105912. 0,00
20240726111328E 0,50
20240728105155. 0,00
20240728110830E 0,50
20240729173459. 0,00
20240729173958. 0,00
20240729183501. 0,00
20240729193003. 0,00
20240729202505. 0,00
20240729212008. 0,00
20240729221510. 0,00
20240729231012. 0,00
20240730000515. 0,00
20240730010017. 0,00
20240730025021. 0,00
20240730034523. 0,00
20240730044026. 0,00
20240730053528. 0,00
20240730063031. 0,00
20240730072533. 0,00
20240730082035. 0,00
20240730084849Q 0,te
20240730115001. 0,00
20240730115148E 0,50
20240730115322E 1,00
20240730115506. 0,00
20240730120003E 0,60
20240731112209E 1,00
20240731112348E 1,00
20240731112548E 1,00
20240731112800E 1,00
]]
-- text1 = text1 .. "\n" .. trim(text101) .. "\n" -- zu Testzwecken
local md5key = md5lib.hash(text1)
local signature = App.crypt('',sec_key1, md5key)
-- local f = io.open('newreport.txt','w')
local text2 = signature .. "\n" .. text1 .. "\n"
my_write_filedata1('newreport.txt',sd_dir..'/newreport.txt',App.crypt('',sec_key3,text2,80),over_write)
-- f:write(text2)
-- f:close()
end
--*************************************************************************
--private
if arg ~= nil and arg[1] == 'tan' then
translation_table = ""
function write_flash_string (x) return end
local x = App:new(1)
print('-----------------------------------------------')
x:compute_tan(arg[2])
print('-----------------------------------------------')
end
--*************************************************************************
--private
if arg ~= nil and arg[1] == 'test1' then
translation_table = ""
local x = App:new(1)
local hh0 = "20220730000001"
local hh1 = x:time(hh0)
print (hh0, hh1)
local hh2 = x:date("%y%m%d%H%M%S",hh1)
local hh3 = x:time(hh2)
print (hh2, hh3)
local hh4 = x:date("%y%m%d%H%M%S",hh3)
local hh5 = x:time(hh4)
print (hh4, hh5)
end
--*************************************************************************
--private
if arg ~= nil and arg[1] == 'test2' then
translation_table = ""
local x = App:new(1)
local hh1 = x:date("%y %m %d %H:%M:%S",tonumber(arg[2]))
print (hh1)
end
--*************************************************************************
--private
if arg ~= nil and arg[1] == 'test3' then
translation_table = ""
local x = App:new(1)
local hh0 = arg[2]
local hh1 = x:time(hh0)
print(hh1)
print("223344 1XX",hh0)
local hh2 = x:date(t("%y%m%d%H%M%S"),hh1)
print("223344 2XX",hh2)
end
--*************************************************************************
--private
if arg ~= nil and arg[1] == 'test4' then
translation_table = ""
local x = App:new(1)
local erg = text_to_numbers("Dönüş")
print(erg)
local erg1 = numbers_to_text(erg)
print(erg1)
print("-----")
local erg2 = replace_diacritic_letters("Dönüş")
print(erg2)
end
--*************************************************************************
--private
if arg ~= nil and arg[1] == 'test5' then
translation_table = ""
local x = App:new(1)
-- local erg = x:crypt(sec_key,"abcjksdhfjvsdflöjsdaöklfjsdöklajföklasdjfökladjsfkölsdjaöklfjdskölafjkdölsajfkölsdafjkölasdjfökldsajfkölasdjfkölasdjöfklsdjakhdsjfhsdjklfhsdjaklfhjdkalhfjdklshafjkdlashfjkldshfjklsdhfjkdlashadhf",80)
local erg = x:crypt(sec_key3,SERIALNUMBER.."83552".."430".."fdsxjkslafzrue")
local uvw = x:decrypt(pub_key3,erg)
print("x-"..erg.."-x")
print("y-"..uvw.."-y")
end
--*************************************************************************
--private
if arg ~= nil and arg[1] == 'test6' then
translation_table = ""
local x = App:new(1)
print(tonumber("0xfff"))
print( string.format("%02x",3251) )
end
--*************************************************************************
--private
if arg ~= nil and arg[1] == 'tt' then
translation_table = ""
local x = App:new(1)
local y = x:date('%H%M',15*3600+20*60)
print(y)
end
--*************************************************************************
--private
if arg ~= nil and arg[1] == 'read' then
translation_table = ""
local x = App:new(1)
local erg = my_read_filedata1(arg[2])
local erg1 = x:decrypt(sec_key3,erg)
print(erg1)
end
--*************************************************************************
--private
if arg ~= nil and arg[1] == 'write' then
translation_table = ""
local x = App:new(1)
local erg = my_read_filedata1(arg[2])
local erg1 = x:crypt(pub_key3,erg,80)
my_write_filedata1(arg[3],arg[3],erg1)
-- print(erg1)
end
--*************************************************************************
--private
if arg ~= nil and arg[1] == 'ty' then
translation_table = ""
local x = App:new(1)
local erg1 = x:crypt(sec_key1,arg[2],80)
print(erg1)
end
--*************************************************************************
--private
--if arg ~= nil and arg[1] == 'voucher' then
-- translation_table = ""
-- local x = App:new(1)
--
-- local erg = my_read_filedata1(arg[2])
-- local erg1 = x:crypt(pub_key1,erg,80)
-- my_write_filedata1("v.c","v.c",trim(erg1).."\n--\n")
--
-- print(erg1)
--
--end
--*************************************************************************
--private
if arg ~= nil and (arg[1] == 'joe' or arg[1] == 'vim' or arg[1] == 'print') then
translation_table = ""
local x = App:new(1)
local erg = my_read_filedata1(arg[2])
local erg1 = x:decrypt(sec_key3,erg)
if arg[1] == 'print' then
print(erg1)
else
my_write_filedata1(arg[2].."~",arg[2].."~",erg1,over_write)
os.execute(arg[1].." "..arg[2].."~")
local erg2 = my_read_filedata1(arg[2].."~")
local erg3 = x:crypt(pub_key3,erg2,80)
my_write_filedata1(arg[2],arg[2],erg3,over_write)
end
-- print(erg1)
end
--**********************************************************************
--private
if arg ~= nil and arg[1] == "xpcall" then
translation_table = ""
local x = App:new(1)
print_symbols()
end
--**********************************************************************
--private
if arg ~= nil and arg[1] == "decodeorder" then
translation_table = ""
local x = App:new(1)
html_parameter = arg[2]
local pos = string.sub(html_parameter,1,1) -- Hineincodieren der serial number modulo 4096
pos = "0x" + pos
pos = tonumber(pos) + 2
local a1 = string.sub(html_parameter,1,pos)
local a2 = string.sub(html_parameter,pos+2,pos+2)
local a3 = string.sub(html_parameter,pos+4,pos+4)
local a4 = string.sub(html_parameter,pos+6)
local sk1 = string.sub(html_parameter,pos+1,pos+1)
local sk2 = string.sub(html_parameter,pos+3,pos+3)
local sk3 = string.sub(html_parameter,pos+5,pos+5)
html_parameter = a1 .. a2 .. a3 .. a4
print( sk1 .. sk2 .. sk3 .. x:decrypt(pub_key1,html_parameter) )
end
--*************************************************************************
--private
if arg ~= nil and arg[1] == "keys" then
-- ak1 = tostring(arg[1])
bk1 = bin.stohex( crypto.public_key( bin.hextos(ak1) ) )
bk2 = bin.stohex( crypto.public_key( bin.hextos(ak2) ) )
sec_key2 = bk1 .. ak2
pub_key2 = bk2 .. ak1
if sec_key ~= sec_key2 or pub_key ~= pub_key2 or arg[2] ~= nil then
local statf = io.open('/proc/self/stat')
math.randomseed(os.time()+os.clock()+tonumber(string.sub(statf:read(),1,4)))
statf:close()
ak1 = {}
ak2 = {}
for i = 1,256,1 do
-- ak1 = ak1 .. tostring(math.random(0,1))
-- ak2 = ak2 .. tostring(math.random(0,1))
table.insert(ak1,tostring(math.random(0,1)))
table.insert(ak2,tostring(math.random(0,1)))
end
ak1 = bin.bintohex(ak1)
ak2 = bin.bintohex(ak2)
bk1 = bin.stohex( crypto.public_key( bin.hextos(ak1) ) )
bk2 = bin.stohex( crypto.public_key( bin.hextos(ak2) ) )
sec_key = bk1 .. ak2
pub_key = bk2 .. ak1
fc = io.open('newkeys.txt','w')
if fc ~= nil then
fc:write('\n')
fc:write('sec_key = \''..sec_key..'\'\n')
fc:write('pub_key = \''..pub_key..'\'\n')
fc:write('ak1 = \''..ak1..'\'\n')
fc:write('ak2 = \''..ak2..'\'\n')
fc:close()
end
else
os.remove('newkeys.txt')
end
end
--*************************************************************************
--private
if arg ~= nil and arg[2] == "rcode" then
text1 = my_read_filedata1(arg[3],sd_dir..'/'..arg[3])
text2 = {}
zeile0 = ""
for zeile in string.gmatch(text1,'[^\n]+') do
zeile = string.gsub(zeile," ","")
if #zeile == 3 and string.find(zeile,"%d%d%d") then
a = 1
zeile0 = ""
elseif #zeile0 < 27 then
zeile = zeile0 .. zeile
zeile0 = zeile
elseif zeile == "" then
zaehler = 0
for zeile in text2 do
zaehler = zaehler + 1
if text2 == "" then
break
end
zeile = string.sub(text2,1,27)
text2 = string.sub(zeile,28)
t2 = bin.stohex( md5lib.has( string.sub(zeile,1,25) ) )
t2 = tonumber("0x"..string.sub(t2,1,3)) % 1000
nr2 = 1 + math.floor(t2/32)
nr3 = 1 + t2 % 32
if string.sub(zeile,26,27) == string.sub(CHAR32,nr2,nr2) .. string.sub(CHAR32,nr3,nr3) then
print(zeile)
else
print(zeile,"<--")
end
end
text2 = {}
zeile0 = ""
else
table.insert(text2,zeile)
end
end
end
--*************************************************************************
--private
if arg ~= nil and arg[1] == "pruefcode" then
translation_table = ""
local x = App:new(1)
x.akt_time = arg[2]
x.amount_in_euro = arg[3]
x.ticketnr = arg[4]
x:voucher_barcode()
local y = x.transmit_nr
print(string.sub(y,1,4) .. " " .. string.sub(y,5,8) .. " " .. string.sub(y,9))
-- x:voucher_barcode_reverse()
end
--*************************************************************************
--private
if arg ~= nil and arg[1] == "sernomod" then
-- ak1 = tostring(arg[1])
print (math.floor(SERIALNUMBERMOD4096 % tonumber(arg[2]) ))
end
--******************************************
--private
if arg ~= nil and arg[1] == "test73" then
e = bin.hextos(arg[2])
print(#arg[2],#e,e)
end
--*************************************************************************
--private
if arg ~= nil and arg[1] == "read_patch" then
function start_timer (a) if a == 12 then load_parts() end end
function load() print(PATCH_plaintext) end
function patch_call () end
function stop_timer () end
function set_value () end
function set_text () end
local jj = load_patch("patch_" .. SERIALNUMBER .. ".enc")
end
--*************************************************************************
--private
if arg ~= nil and arg[1] == "ttime" then
zaehler = 1
st = 1751866900
math.randomseed(5876)
while zaehler < 200 do
st = st + math.random(1000,5000)
print(math.random(20,60))
cu = math.random(1,10)
cu = string.sub("EQX.......",cu,cu)
print(App.date('',"%y%m%d%H%M%S",st) .. cu .. " " .. string.format("%3.2f",math.random(2,60)/10))
zaehler = zaehler + 1
end
end
--*************************************************************************
--private
if arg ~= nil and arg[1] == "signatable" then -- Ausdruck der Signature Lookup Table
local x = App:new(1)
print( create_signa_table( tonumber(arg[2]),120 ))
if arg[3] == "1" then
print(VOUCHER_M,PRINT_BF_EXIST)
end
end
--*************************************************************************
--private
if arg ~= nil and arg[1] == "report" then -- manuelles Erstellen eines Ticket Report
translation_table = ""
local x = App:new(1)
function start_timer () end
function flush_flash () end
-- function App.write () end
x.squeeze_out = 1
x.extern_reportnr = 5
x.extern_report = {}
x.extern_report["reports"] = {}
-- x.erzeugungsinfo = "abcde"
for zeile in string.gmatch(my_read_filedata1(arg[2]),'[^\n]+') do
table.insert(x.extern_report["reports"],zeile)
end
x.extern_report['header'] = {}
table.insert(x.extern_report['header'],"Dies ist ein Test")
x.extern_report['reportnr'] = "004"
x.extern_report['datum'] = "08.07.2024"
x.extern_report['zeit'] = "12:29"
x.extern_report['who'] = "12:29"
x.extern_report['ust'] = "19.1"
x.extern_report['stitle'] = "Signatur "
x.extern_report['dformat'] = "%d.%m.%y"
x.extern_report['zformat'] = "%H:%M"
-- print(#x.extern_report)
x:p_generate_report()
end
--*************************************************************************
--private
if arg ~= nil and arg[1] == "euklid" then -- manuelles Erstellen eines Ticket Report
translation_table = ""
local x = App:new(1)
print(x:euklid(tonumber(arg[2]),tonumber(arg[3])))
end
--*************************************************************************
--private
if arg ~= nil and arg[1] == "test9" then
xx = string.format("%1x",tonumber(arg[2]))
print(xx)
xx1 = bin.hextos(xx)
print("LL",#xx1)
xx2 = base32.encode(xx1)
print(xx,xx2)
end
--************************************************************************
--private
if arg ~= nil and arg[1] == "tt5" then
v,a = custom_voucher (tonumber(arg[2]),tonumber(arg[3]))
print(a)
end
--************************************************************************
--private
if arg ~= nil and arg[1] == "bm" then
local gg,hh = qrc.qrcode("FGDHJskaDGDKGGdhksgadhkjgsHgsafasdafdafdasJKGSJHKAgfffffkfgkhfgkfgkfgdghksjaGDHKASGdhksgDGSdjhkgshakDGSHKdgjhksaDGHKSdghkjsgaDHKJGkghasjkDGHKSgdhkjasgdhksjaKTR6543",4)
for i,x in ipairs(hh) do
x1 = ""
for j,y in ipairs(x) do
if y > 0 then
x1 = x1 .. "XX"
else
x1 = x1 .. " "
end
end
print(x1)
end
local ff,vv = App.qr_to_bitmap('',2,hh,9)
local qr_s = App.qrcode_to_string('',hh)
-- local qr_hex = bin.stohex(qr_s)
local qr_27 = base27_encode(qr_s)
-- print(qr_hex)
-- print(qr_27)
local erg = qr_27
local zeilenlaenge = 32
local padding = #erg % zeilenlaenge
if padding > 0 then
erg = erg .. string.rep("E",zeilenlaenge-padding)
end
local erg1 = ""
while zeilenlaenge ~= nil do
erg1 = erg1 .. string.sub(erg,1,zeilenlaenge) .. "\n"
erg = string.sub(erg,zeilenlaenge+1)
if #erg == 0 then
break
end
end
print(erg1.."\n",#erg1/33,#hh)
--for i=1,#vv do
-- a,b = string.gsub(string.gsub(vv[i],"0"," "),"1","X")
-- print(a)
--end
end
--************************************************************************
--private
if arg ~= nil and arg[1] == "zip1" then
text3 = [[
12park
0553227 / 009
12.07.2024 08:44:32
09.07.2024 17:50 1,40 E
--++=----- -------
09:18 0,bo Q 13,90 E
09:42 0,te Q
09:42 0,ka Q 11.07.2024
10:07 0,ka Q ----------
10:10 0,50 E 08:39 0,bo Q
10:28 1,00 E 08:52 0,bo Q
10:28 2,00 E 08:57 0,ka Q
10:41 0,50 E 09:04 2,20 E
10:55 0,ka Q 09:09 0,te Q
11:06 1,50 E 10:03 1,00 E
11:08 2,00 E 10:16 0,ka Q
11:35 2,00 E 10:40 2,00 E
15:20 0,50 E 10:47 0,50 E
15:40 0,60 E 10:53 1,30 E
15:40 0,bo Q 11:03 1,50 E
16:19 0,50 E 11:03 2,00 E
19:38 4,00 E 11:14 0,50 E
19:52 3,70 E 11:20 1,00 E
------- 11:23 0,50 E
18,80 E 12:08 0,50 E
12:08 0,em Q
10.07.2024 12:08 0,em Q
---------- 12:22 2,00 E
07:59 1,00 E 12:31 0,50 E
08:40 0,te Q 12:48 0,bo Q
08:41 0,te Q 14:02 0,50 E
09:03 0,bo Q 14:14 0,ka Q
09:15 0,em Q 14:46 2,00 E
09:15 0,em Q 15:04 1,00 E
09:30 0,50 E 15:18 1,00 E
09:53 1,00 E 15:27 2,00 E
10:10 2,00 E 15:53 1,50 E
10:12 1,00 E 17:00 2,00 E
10:24 1,00 E -------
11:20 1,00 E 25,50 E
11:57 1,00 E
12:01 0,50 E NETTO: 48,91
12:53 0,bo Q USt19% 9,29
12:56 0,ka Q TOTAL: 58,20
14:21 1,50 E
14:52 1,00 E
16:28 1,00 E
www.12park.de
]]
text4 = App.make_signa_report(App,text3,"",5)
print(text4)
end
--************************************************************************
--private
if arg ~= nil and arg[1] == 'test704' then
math.randomseed(tonumber(os.time()%875647))
rr = math.random(1,100010001000100)
rr1 = string.format("%0x",rr)
rr2 = table.concat( bin.hextobin(string.sub(rr1,3)) )
rr = math.random(1,100010001000100)
rr1 = string.format("%0x",rr)
rr2 = rr2 .. table.concat( bin.hextobin(string.sub(rr1,3)) )
text1 = rr2
print(text1)
text2 = base62_encode(text1)
print(text2,#text2)
-- text2 = "10"
text3 = base62_decode(text2)
print(text3)
if pad_down(text1,"0") == text3 then
print("OK")
end
end
--************************************************************************
--private
if arg ~= nil and arg[1] == "qrenc" then
text =
"fdfdlkafhldahfjkldhafkldhajlfhdjlsfhjdshlfjdhlasfjdhlajfhdjalfhttttttttffffffff4444" ..
"fdfdlkafhldahfjkldhafkldhajlfhdjlsfhjdshlfjdhlasfjdhlajfhdjalfhttttttttffffffff4444"
text1 = App.crypt('',sec_key1,text,32)
print(text1)
end
if arg ~= nil and arg[1] == "ttt" then
local text6 = [[
dsadfadsaDSAdsaDSADAFGDJKFAgjdfsgFDDKFÖDAGJFÄDAJKÄFGJAFGÄKRJWÄKGJFKÄSJGÄVKÄLJVKÄRMVKÄMKÄLNäjsdkvädaävndkaälvnfklaänvklfäanvaä
dsadfadsaDSAdsaDSADAFGDJKFAgjdfsgFDDKFÖDAGJFÄDAJKÄFGJAFGÄKRJWÄKGJFKÄSJGÄVKÄLJVKÄRMVKÄMKÄLNäjsdkvädaävndkaälvnfklaänvklfäanvaä
dsadfadsaDSAdsaDSADAFGDJKFAgjdfsgFDDKFÖDAGJFÄDAJKÄFGJAFGÄKRJWÄKGJFKÄSJGÄVKÄLJVKÄRMVKÄMKÄLNäjsdkvädaävndkaälvnfklaänvklfäanvaä
dsadfadsaDSAdsaDSADAFGDJKFAgjdfsgFDDKFÖDAGJFÄDAJKÄFGJAFGÄKRJWÄKGJFKÄSJGÄVKÄLJVKÄRMVKÄMKÄLNäjsdkvädaävndkaälvnfklaänvklfäanvaä
dsadfadsaDSAdsaDSADAFGDJKFAgjdfsgFDDKFÖDAGJFÄDAJKÄFGJAFGÄKRJWÄKGJFKÄSJGÄVKÄLJVKÄRMVKÄMKÄLNäjsdkvädaävndkaälvnfklaänvklfäanvaä
dsadfadsaDSAdsaDSADAFGDJKFAgjdfsgFDDKFÖDAGJFÄDAJKÄFGJAFGÄKRJWÄKGJFKÄSJGÄVKÄLJVKÄRMVKÄMKÄLNäjsdkvädaävndkaälvnfklaänvklfäanvaä
dsadfadsaDSAdsaDSADAFGDJKFAgjdfsgFDDKFÖDAGJFÄDAJKÄFGJAFGÄKRJWÄKGJFKÄSJGÄVKÄLJVKÄRMVKÄMKÄLNäjsdkvädaävndkaälvnfklaänvklfäanvaä
dsadfadsaDSAdsaDSADAFGDJKFAgjdfsgFDDKFÖDAGJFÄDAJKÄFGJAFGÄKRJWÄKGJFKÄSJGÄVKÄLJVKÄRMVKÄMKÄLNäjsdkvädaävndkaälvnfklaänvklfäanvaä
dsadfadsaDSAdsaDSADAFGDJKFAgjdfsgFDDKFÖDAGJFÄDAJKÄFGJAFGÄKRJWÄKGJFKÄSJGÄVKÄLJVKÄRMVKÄMKÄLNäjsdkvädaävndkaälvnfklaänvklfäanvaä
]]
text6 = " "
print(text6)
local text7 = base27_encode(text6)
print(text7)
local text8 = base27_decode(text7)
print(text8)
end
--**********************************************************************
-- CONSOLE_AREA_END
--**********************************************************************
-- TRANSLATIONS_BEGIN
--**********************************************************************
translation_table = [[
;LCODE;DE;EN;FR;IT;DE;ES;PT;NL;FR;FL;TR;PL;RO;DE;FR;IT;CS;SK;HR;
;LANG;Deutsch;English;Français;Italiano;Deutsch;Español;Português;Nederlands;Français;Flaams;Türkçe;Polski;Română;Deutsch;Français;Italiano;Čeština;Slovenská;Hrvatski;
;S2F270;Bitte Münzen einwerfen;Please insert coins;Veuillez insérer des;Inserire le monete;Bitte Münzen einwerfen;Por favor inserte;Por favor, insira moedas;Gelieve munten;Veuillez insérer des;Gelieve munten;Lütfen bozuk para girin;Proszę włożyć monety;Vă rugăm să;Bitte Münzen einwerfen;Veuillez insérer des;Inserire le monete;Vložte prosím mince;Vložte mince;Ubacite kovanice;
;S2F271;-;-;pièces;-;-;monedas;-;in te voeren;pièces;in te voeren;-;-;introduceți monede;-;pièces;-;-;-;-;
;S2F171;Bargeldlos bezahlen:;Cashless payment:;Payer sans argent liquide:;Pagamento senza contanti:;Bargeldlos bezahlen:;Pago sin efectivo:;Pagamentos sem dinheiro:;Contant betalen:;Payer sans argent liquide:;Contant betalen:;Nakitsiz ödemeler:;Płatności bezgotówkowe:;Plăți fără numerar:;Bargeldlos bezahlen:;Payer sans argent liquide:;Pagamento senza contanti:;Bezhotovostní platby:;Bezhotovostné platby:;Bezgotovinsko plaćanje;
;S3F171;Bargeldlos bezahlen:;Cashless payment:;Payer sans argent liquide:;Pagamento senza contanti:;Bargeldlos bezahlen:;Pago sin efectivo:;Pagamentos sem dinheiro:;Contant betalen:;Payer sans argent liquide:;Contant betalen:;Nakitsiz ödemeler:;Płatności bezgotówkowe:;Plăți fără numerar:;Bargeldlos bezahlen:;Payer sans argent liquide:;Pagamento senza contanti:;Bezhotovostní platby:;Bezhotovostné platby:;Bezgotovinsko plaćanje;
;S15F171;Bargeldlos bezahlen:;Cashless payment:;Payer sans argent liquide:;Pagamento senza contanti:;Bargeldlos bezahlen:;Pago sin efectivo:;Pagamentos sem dinheiro:;Contant betalen:;Payer sans argent liquide:;Contant betalen:;Nakitsiz ödemeler:;Płatności bezgotówkowe:;Plăți fără numerar:;Bargeldlos bezahlen:;Payer sans argent liquide:;Pagamento senza contanti:;Bezhotovostní platby:;Bezhotovostné platby:;Bezgotovinsko plaćanje;
;S3F271;Parkzeit;Parking time;Temps de stationnement;Tempo di parcheggio;Parkzeit;Tiempo de estacionamiento;Tempo de estacionamento;Parkeertijd;Temps de stationnement;Parkeertijd;Park etme zamanı;Czas parkowania;Timp de parcare;Parkzeit;Temps de stationnement;Tempo di parcheggio;Doba parkování;Čas parkovania;Trajanje parkiranja;
;A3F271;Anzahl Tickets;Total count of tickets;Nombre total de billets;Numero totale di biglietti;Anzahl Tickets;Recuento total de tickets;Contagem total de bilhetes;Totaal aantal tickets;Nombre total de billets;Totaal aantal tickets;Toplam bilet sayısı;Łączna liczba biletów;Numărul total de bilete;Anzahl Tickets;Nombre total de billets;Numero totale di biglietti;Celkový počet vstupenek;Skupno število vstopnic;Ukupan broj ulaznica;
;S3F270;Für Ticket Knopf drücken;Press button for ticket;Appuyez sur le bouton;Premere il pulsante per il biglietto;Für Ticket Knopf drücken;Presione el botón;Pressione o botão;Druk op de knop voor ticket;Appuyez sur le bouton;Druk op de knop voor ticket;Bilet için butona basın;Naciśnij przycisk,;Apăsați butonul;Für Ticket Knopf drücken;Appuyez sur le bouton;Premere il pulsante per il biglietto;Stiskněte tlačítko pro jízdenku;Stlačte tlačidlo pre lístok;Za kartu,;
;S3F277;-;-;pour le billet;-;-;para el boleto;para o bilhete;-;pour le billet;-;-;aby otrzymać bilet;pentru bilet;-;pour le billet;-;pro jízdenku;-;pritisnite gumb;
;S3F204;Nochmal;Again;Encore;Ancora;Nochmal;De nuevo;De novo;Opnieuw;Encore;Opnieuw;Tekrar;Ponownie;Din nou;Nochmal;Encore;Ancora;Zvonu;Znova;Opet;
;S3F278;Bitte Münzen einwerfen;Please insert coins;Veuillez insérer des;Inserire le monete;Bitte Münzen einwerfen;Por favor inserte;Por favor, insira moedas;Gelieve munten;Veuillez insérer des;Gelieve munten;Lütfen bozuk para girin;Proszę włożyć monety;Vă rugăm să;Bitte Münzen einwerfen;Veuillez insérer des;Inserire le monete;Vložte prosím mince;Vložte mince;Ubacite kovanice;
;S3F279;-;-;pièces;-;-;monedas;-;in te voeren;pièces;in te voeren;-;-;introduceți monede;-;pièces;-;-;-;-;
;BITTEMUENZENA;Bitte Münzen einwerfen;Please insert coins;Veuillez insérer des;Inserire le monete;Bitte Münzen einwerfen;Por favor inserte;Por favor, insira moedas;Gelieve munten;Veuillez insérer des;Gelieve munten;Lütfen bozuk para girin;Proszę włożyć monety;Vă rugăm să;Bitte Münzen einwerfen;Veuillez insérer des;Inserire le monete;Vložte prosím mince;Vložte mince;Ubacite kovanice;
;BITTEMUENZENB;-;-;pièces;-;-;monedas;-;in te voeren;pièces;in te voeren;-;-;introduceți monede;-;pièces;-;-;-;-;
;KNOPFDRUECKENA;Für Ticket Knopf drücken;Press button for ticket;Appuyez sur le bouton;Premere il pulsante per il biglietto;Für Ticket Knopf drücken;Presione el botón;Pressione o botão;Druk op de knop voor ticket;Appuyez sur le bouton;Druk op de knop voor ticket;Bilet için butona basın;Naciśnij przycisk,;Apăsați butonul;Für Ticket Knopf drücken;Appuyez sur le bouton;Premere il pulsante per il biglietto;Stiskněte tlačítko pro jízdenku;Stlačte tlačidlo pre lístok;Za kartu,;
;KNOPFDRUECKENB;-;-;pour le billet;-;-;para el boleto;para o bilhete;-;pour le billet;-;-;aby otrzymać bilet;pentru bilet;-;pour le billet;-;pro jízdenku;-;pritisnite gumb;
;S3F267;Ticket wird gedruckt;Ticket is printed;Le billet est imprimé;Il biglietto è stampato;Ticket wird gedruckt;Se imprime el boleto;O bilhete é impresso;Ticket is afgedrukt;Le billet est imprimé;Ticket is afgedrukt;Bilet basıldı;Bilet jest drukowany;Biletul este tipărit;Ticket wird gedruckt;Le billet est imprimé;Il biglietto è stampato;Lístek je vytištěn;Lístok je vytlačený;Karta se ispisuje;
;S3F201;Sondertarif;Special rate;Tarif spécial;Tasso speciale;Sondertarif;Tarifa especial;Tarifa especial;Speciaal tarief;Tarif spécial;Speciaal tarief;Özel tarife;Taryfa specjalna;Tarif special;Sondertarif;Tarif spécial;Tasso speciale;Speciální sazba;Špeciálna tarifa;Posebna stopa;
;S5F217;Hauptmenü;Main menu;Menu principal;Menu principale;Hauptmenü;Menú principal;Menu principal;Hoofdmenu;Menu principal;Hoofdmenu;Ana menü;Menu główne;Meniu principal;Hauptmenü;Menu principal;Menu principale;Hlavní menu;Hlavné menu;Glavni izbornik;
;S5F202;Lokale Einstellungen;Local settings;Paramètres locaux;Impostazioni locali;Lokale Einstellungen;Configuraciones locales;Configurações locais;Lokale instellingen;Paramètres locaux;Lokale instellingen;Yerel ayarlar;Ustawienia lokalne;Setari locale;Lokale Einstellungen;Paramètres locaux;Impostazioni locali;Místní nastavení;Miestne nastavenia;Lokalne postavke;
;S5F201;Verfügbare Punkte:;Available points:;Points disponibles:;Punti disponibili:;Verfügbare Punkte:;Puntos disponibles:;Pontos disponíveis:;Beschikbare punten:;Points disponibles:;Beschikbare punten:;Mevcut noktalar:;Dostępne punkty:;Puncte disponibile:;Verfügbare Punkte:;Points disponibles:;Punti disponibili:;Dostupné body:;Dostupné body:;Raspoloživi bodovi;
;S5F212;Tarif;Tariff;Tarif;Tariffa;Tarif;Tarifa;Tarifa;Tarief;Tarif;Tarief;Tarife;Taryfa;Tarifar;Tarif;Tarif;Tariffa;Tarif;Tarifa;Tarifa;
;S5F213;Protokoll;Protocol;Protocole;Protocollo;Protokoll;Protocolo;Protocolo;Protocol;Protocole;Protocol;Protokol;Protokół;Protocol;Protokoll;Protocole;Protocollo;Protokol;Protokol;Protokol;
;S5F205;Erweitert;Extended;Additionnel;Esteso;Erweitert;Extendido;Avançado;Verlengd;Additionnel;Verlengd;Ileri;Zaawansowane;Avansat;Erweitert;Additionnel;Esteso;Pokročilý;Pokročilé;Produljenje;
;S5F299;Beenden;Finish;Rompre;Fine;Beenden;Finalizar;Terminar;Af hebben;Rompre;Af hebben;Son;Koniec;Sfârşit;Beenden;Rompre;Fine;Konec;Koniec;Kraj;
;S5F204;Tickets seit Neustart:;Tickets since restart:;Billets depuis redémarrage:;Biglietti dal riavvio:;Tickets seit Neustart:;Entradas desde el reinicio:;Bilhetes desde o reinício:;Tickets sinds herstart:;Billets depuis redémarrage:;Tickets sinds herstart:;Yeniden başlatmadan bu yana biletler:;Bilety od restartu:;Bilete de la repornire:;Tickets seit Neustart:;Billets depuis redémarrage:;Biglietti dal riavvio:;Vstupenky od restartu:;Lístky od reštartu:;Karte prije ponovnog pokretanja;
;S6F203;Mindestbetrag;Minimum amount;Montant minimal;Quantità minima;Mindestbetrag;Monto minimo;Valor mínimo;Minimale hoeveelheid;Montant minimal;Minimale hoeveelheid;Minimum miktar;Stawka minimalna;Cantitatea minima;Mindestbetrag;Montant minimal;Quantità minima;Minimální množství;Minimálna suma;Minimalan iznos;
;S6F208;Preis pro Stunde;Price per hour;Prix par heure;Prezzo all'ora;Preis pro Stunde;Precio por hora;Preço por hora;Prijs per uur;Prix par heure;Prijs per uur;Saatlik fiyat;Cena za godzinę;Pret pe ora;Preis pro Stunde;Prix par heure;Prezzo all'ora;Cena za hodinu;Cena za hodinu;Cjena po satu;
;S6F201;Mehrwertsteuer %;VAT %;T.V.A. %;IVA %;Mehrwertsteuer %;IVA %;Imposto Valor Acr. %;BTW %;T.V.A. %;BTW %;Katma değer Vergisi %;Podatek obrotowy %;Taxa pe valoare adaugata %;Mehrwertsteuer %;T.V.A. %;IVA %;Daň z přidané hodnoty %;Daň z pridanej hodnoty %;PDV %;
;S6F204;Tarif;Tariff;Tarif;Tariffa;Tarif;Tarifa;Tarifa;Tarief;Tarif;Tarief;Tarife;Taryfa;Tarifar;Tarif;Tarif;Tariffa;Tarif;Tarifa;Tarifa;
;S6F207;Höchstparkdauer;Maximum parking;Maximale de;Parcheggio;Höchstparkdauer;Estacionamiento;Tempo máx. estacion.;Maximaal parkeren;Maximale de;Maximaal parkeren;Maksimum park süresi;Maksymalny czas;Timp maxim de;Höchstparkdauer;Maximale de;Parcheggio;Maximální doba parkování;Maximálny čas parkovania;Maksimalno trajanje;
;S6F206;-;-;stationnement;massimo;-;máximo;-;-;stationnement;-;-;parkowania;parcare;-;stationnement;massimo;-;-;parkinga;
;S6F211;Tagesticket;Day ticket;Billet journalier;Biglietto giornaliero;Tagesticket;Boleto de un día;Bilhete diário;Dagkaart;Billet journalier;Dagkaart;Günlük bilet;Bilet dzienny;Bilet de zi;Tagesticket;Billet journalier;Biglietto giornaliero;Denní jízdenka;Denný lístok;Dnevna karta;
;S6F215;Stunden;Hours;Heures;Ore;Stunden;Horas;Horas;Uren;Heures;Uren;Saat;Godziny;Ore;Stunden;Heures;Ore;Hodin;Hodiny;Sati;
;S6F216;Minuten;Minutes;Minutes;Minuti;Minuten;Minutos;Minutos;Minuten;Minutes;Minuten;Dakika;Minuty;Minute;Minuten;Minutes;Minuti;Minut;Minút;Minute;
;S6F217;Nein;No;Non;No;Nein;No;Não;Nee;Non;Nee;Hayır;Nie;Nu;Nein;Non;No;Ne;Nie;Ne;
;S6F218;Ja;Yes;Oui;Sì;Ja;Sí;Sim;Ja;Oui;Ja;Evet;Tak;Da;Ja;Oui;Sì;Ano;Áno;Da;
;S6F220;Münzen;Coins;Monnaie;Monete;Münzen;Monedas;Moedas;Munten;Monnaie;Munten;Madeni paralar;Monety;Monede;Münzen;Monnaie;Monete;Mince;Mince;Kovanice;
;S6F221;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QR kod;
;S6F222;Mü+QR;Co+QR;Mo+QR;Mo+QR;Mü+QR;Mo+QR;Mo+QR;Mu+QR;Mo+QR;Mu+QR;Ma+QR;Mo+QR;Mo+QR;Mü+QR;Mo+QR;Mo+QR;Mi+QR;Mi+QR;Ko+QR;
;S6F233;Zahlungsweise;Payment mode;Mode de paier;Metodo di pagare;Zahlungsweise;Modo de pago;Forma de pagar;Betaalmethode;Mode de paier;Betaalmethode;Ödeme şekli;Metoda płatności;Metodă de plată;Zahlungsweise;Mode de paier;Metodo di pagare;Způsob platby;Spôsob platby;Način plaćanja;
;S6F240;Tabelle;Table;Tableau;Tavolo;Tabelle;Tabla;Mesa;Tabel;Tableau;Tabel;Tabel;Tabela;Tabel;Tabelle;Tableau;Tavolo;Tabulka;Tabuľka;Tabela;
;S6F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S6F219;Weiter zu Tarif 2;Go to tariff 2;Aller au tarif 2;Vai alla tariffa 2;Weiter zu Tarif 2;Ir a la tarifa 2;Continuar Tarifa 2;Ga naar tarief 2;Aller au tarif 2;Ga naar tarief 2;Tarife 2 ayarları;Przejdź do taryfy 2;Continuați la Tariful 2;Weiter zu Tarif 2;Aller au tarif 2;Vai alla tariffa 2;Pokračujte na tarif 2;Prejsť na tarifu 2;Izaberi tarifu 2;
;S7F204;Erweitert;Extended;Additionnel;Esteso;Erweitert;Extendido;Avançado;Verlengd;Additionnel;Verlengd;Ileri;Zaawansowane;Avansat;Erweitert;Additionnel;Esteso;Pokročilý;Pokročilé;Produljenje;
;S7F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S7F201;TAN Freigabe für Tickets;Charging TAN for tickets;TAN de recharge des billets;Addebito TAN per i biglietti;TAN Freigabe für Tickets;Cobro de TAN por boletos;Recarga TAN para bilhetes;TAN opladen voor tickets;TAN de recharge des billets;TAN opladen voor tickets;Biletler için TAN sürümü;Wydanie TAN dla biletów;Eliberare TAN pentru bilete;TAN Freigabe für Tickets;TAN de recharge des billets;Addebito TAN per i biglietti;TAN uvolnění pro vstupenky;Uvoľnenie TAN na lístky;Naplaćivanje TAN broja za karte;
;S7F212;PIN Verwaltung;PIN Administration;Administration PIN;Amministrazione PIN;PIN Verwaltung;Administración de PIN;Gestão de PIN;PIN beheer;Administration PIN;PIN beheer;PIN yönetimi;Zarządzanie kodami PIN;Management PIN;PIN Verwaltung;Administration PIN;Amministrazione PIN;Správa PIN;Správa PIN;PIN izbornik;
;S7F203;Neue PIN Liste;New PIN list;Nouvelle liste des codes PIN;Nuovo elenco PIN;Neue PIN Liste;Nueva lista de PIN;Nova lista PIN;Nieuwe PIN lijst;Nouvelle liste des codes PIN;Nieuwe PIN lijst;Yeni PIN listesi;Nowa lista PIN;Listă PIN nouă;Neue PIN Liste;Nouvelle liste des codes PIN;Nuovo elenco PIN;Nový seznam PIN;Nový zoznam PIN;Nova PIN lista;
;S7F208;Gerät;Device;Appareil;Dispositivo;Gerät;Dispositivo;Dispositivo;Apparaat;Appareil;Apparaat;Cihaz;Urządzenie;Dispozitiv;Gerät;Appareil;Dispositivo;Přístroj;Zariadenie;Uređaj;
;S8F202;Sprache;Language;Langue;Lingua;Sprache;Idioma;Língua;Taal;Langue;Taal;Dil;Język;Limba;Sprache;Langue;Lingua;Jazyk;Jazyk;Jezik;
;S8F203;Standort 1;Location 1;Emplacement 1;Posizione 1;Standort 1;Ubicación 1;Local 1;Locatie 1;Emplacement 1;Locatie 1;Konum 1;Lokalizacja 1;Locația 1;Standort 1;Emplacement 1;Posizione 1;Umístění 1;Umiestnenie 1;Lokacija 1;
;S8F204;Lokale Einstellungen;Local settings;Paramètres locaux;Impostazioni locali;Lokale Einstellungen;Configuraciones locales;Configurações locais;Lokale instellingen;Paramètres locaux;Lokale instellingen;Yerel ayarlar;Ustawienia lokalne;Setari locale;Lokale Einstellungen;Paramètres locaux;Impostazioni locali;Místní nastavení;Miestne nastavenia;Lokalne postavke;
;S8F207;Datum;Date;Date;Data;Datum;Fecha;Encontro;Datum;Date;Datum;Tarih;Data;Data;Datum;Date;Data;Datum;Dátum;Datum;
;S8F206;Zeit;Time;Temps;Volta;Zeit;Tiempo;Tempo;Tijd;Temps;Tijd;Zaman;Czas;Timp;Zeit;Temps;Volta;Čas;Čas;Vrijeme;
;S8F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S8F205;Standort 2;Location 2;Emplacement 2;Posizione 2;Standort 2;Ubicación 2;Local 2;Locatie 2;Emplacement 2;Locatie 2;Konum 2;Lokalizacja 2;Locația 2;Standort 2;Emplacement 2;Posizione 2;Umístění 2;Umiestnenie 2;Lokacija 2;
;S8F211;Standort 3;Location 3;Emplacement 3;Posizione 3;Standort 3;Ubicación 3;Local 3;Locatie 3;Emplacement 3;Locatie 3;Konum 3;Lokalizacja 3;Locația 3;Standort 3;Emplacement 3;Posizione 3;Umístění 3;Umiestnenie 3;Lokacija 3;
;S8F215;Protokolle:;Protocols:;Protocoles:;Protocoll:;Protokolle:;Protocolos:;Protocolos:;Protocollen:;Protocoles:;Protocollen:;Kütükler:;Protokoły:;Protocoalele:;Protokolle:;Protocoles:;Protocoll:;Protokoly:;Protokoly:;Protokol;
;S8F216;DE;EN;FR;IT;DE;ES;PT;NL;FR;NL;TR;PL;RO;DE;FR;IT;CS;SK;HR;
;S9F213;Drucken...;Print...;Imprimer...;Stampa...;Drucken...;Impresión...;Imprimir...;Afdrukken...;Imprimer...;Afdrukken...;Yazdır...;Nacisnąć...;Imprima...;Drucken...;Imprimer...;Stampa...;Tlačit...;Stlačiť...;Ispisivanje...;
;S9F204;Protokoll;Protocol;Protocole;Protocollo;Protokoll;Protocolo;Protocolo;Protocol;Protocole;Protocol;Protokol;protokół;Protocol;Protokoll;Protocole;Protocollo;Protokol;Protokol;Protokol;
;S9F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S9F203;Zeilen pro Seite;Lines per page;Lignes par page;Righe per pagina;Zeilen pro Seite;Líneas por página;Linhas por página;Regels per pagina;Lignes par page;Regels per pagina;Sayfa başına satır;Wierszy na stronę;Linii pe pagină;Zeilen pro Seite;Lignes par page;Righe per pagina;Řádků na stránku;Riadkov na stránku;Linije po stranici;
;S9F202;Protokollzähler:;Protocol counter:;Compteur de journaux:;Contatore protocollo:;Protokollzähler:;Contador de protocolo:;Contador protoc.:;Protocolteller:;Compteur de journaux:;Protocolteller:;Günlük sayacı:;Licznik protokołów:;Contor de jurnal:;Protokollzähler:;Compteur de journaux:;Contatore protocollo:;Počítadlo protokolu:;Počítadlo denníka:;Brojač protokola;
;S9F205;Report wird erstellt...;Ticket is created...;Le report est creé...;Il biglietto è stato creato...;Report wird erstellt...;Se crea el ticket...;O relatório é criado...;Ticket is aangemaakt...;Le report est creé...;Ticket is aangemaakt...;Rapor oluşturuluyor...;Raport jest tworzony...;Raportul este creat...;Report wird erstellt...;Le report est creé...;Il biglietto è stato creato...;Sestava se vytváří...;Správa sa vytvára...;Karta se kreira...;
;S10F270;Gerät nicht betriebsbereit;Device not ready;Appareil non en service;Dispositivo non pronto;Gerät nicht betriebsbereit;Dispositivo no listo;Dispositivo não está pronto;Apparaat niet gereed;Appareil non en service;Apparaat niet gereed;Cihaz çalışmaya hazır değil;Urządzenie nie jest gotowe do pracy;Aparatul nu este pregătit pentru funcționare;Gerät nicht betriebsbereit;Appareil non en service;Dispositivo non pronto;Zařízení není připraveno k provozu;Zariadenie nie je pripravené;Uređaj nije spreman;
;S11F280;Sprachauswahl;Language selection;Sélection de la langue;Selezione della lingua;Sprachauswahl;Selección de idioma;Seleção de idioma;Taal selectie;Sélection de la langue;Taal selectie;Dil seçimi;Wybór języka;Alegerea limbii;Sprachauswahl;Sélection de la langue;Selezione della lingua;Výběr jazyka;Výber jazyka;Odabir jezika;
;S11F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S12F280;PIN Eingabe;PIN entry;Entrée PIN;Inserimento PIN;PIN Eingabe;Entrada de PIN;Entrada PIN;PIN invoer;Entrée PIN;PIN invoer;Pin girişi;PIN wpis;Intrare PIN;PIN Eingabe;Entrée PIN;Inserimento PIN;Zadání PIN;Zadanie PIN;Unos pina;
;S12F202;Ungültige PIN;Invalid PIN;Code PIN invalide;PIN non valido;Ungültige PIN;PIN no válido;PIN inválido;Ongeldige pincode;Code PIN invalide;Ongeldige pincode;Geçersiz PIN;Nieprawidłowy PIN;PIN nevalid;Ungültige PIN;Code PIN invalide;PIN non valido;Neplatný PIN;Neplatný PIN;Neispravan PIN;
;S14F280;TAN Freigabe;TAN validation;TAN validation;Convalida TAN;TAN Freigabe;validación TAN;Liberação TAN;TAN-validatie;TAN validation;TAN-validatie;TAN sürümü;Wydanie TAN;Eliberare TAN;TAN Freigabe;TAN validation;Convalida TAN;Uvolnění TAN;Uvoľnenie TAN;Potvrda TAN broja;
;S14F217;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S14F207;TAN anfordern;Order a TAN;Commander un TAN;Ordina un TAN;TAN anfordern;Pide un TAN;Solicitar TAN;Bestel een TAN;Commander un TAN;Bestel een TAN;TAN talep et;Poproś o TAN;Solicitați TAN;TAN anfordern;Commander un TAN;Ordina un TAN;Vyžádejte si TAN;TAN objednávka;Zatraži TAN broj;
;S15F270;Bitte Münzen einwerfen;Please insert coins;Veuillez insérer des;Si prega di;Bitte Münzen einwerfen;Por favor inserte;Por favor, insira moedas;Gelieve munten;Veuillez insérer des;Gelieve munten;Lütfen bozuk para girin;Proszę włożyć monety;Vă rugăm să;Bitte Münzen einwerfen;Veuillez insérer des;Si prega di;Vložte prosím mince;Vložte mince;Molimo umetnite kovanice;
;S15F271;-;-;pièces;inserire monete;-;monedas;-;in te voeren;pièces;in te voeren;-;-;introduceți monede;-;pièces;inserire monete;-;-;-;
;S16F270;Bitte Münzen einwerfen;Please insert coins;Veuillez insérer des;Si prega di;Bitte Münzen einwerfen;Por favor inserte;Por favor, insira moedas;Gelieve munten;Veuillez insérer des;Gelieve munten;Lütfen bozuk para girin;Proszę włożyć monety;Vă rugăm să;Bitte Münzen einwerfen;Veuillez insérer des;Si prega di;Vložte prosím mince;Vložte mince;Molimo umetnite kovanice;
;S16F271;-;-;pièces;inserire monete;-;monedas;-;in te voeren;pièces;in te voeren;-;-;introduceți monede;-;pièces;inserire monete;-;-;-;
;S18F262;PIN Verwaltung;PIN Administration;Administration PIN;Amministrazione PIN;PIN Verwaltung;Administración de PIN;Gestão de PIN;PIN beheer;Administration PIN;PIN beheer;PIN yönetimi;Zarządzanie PIN;Management PIN;PIN Verwaltung;Administration PIN;Amministrazione PIN;Správa PIN;Správa PIN;PIN izbornik;
;S18F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarc.;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S21F204;Neue PIN Liste;New PIN list;Liste des codes PIN;Nuovo elenco PIN;Neue PIN Liste;Nueva lista de PIN;Nova lista PIN;Nieuwe PIN lijst;Liste des codes PIN;Nieuwe PIN lijst;Yeni PIN listesi;Nowa lista PIN;Listă PIN nouă;Neue PIN Liste;Liste des codes PIN;Nuovo elenco PIN;Nový seznam PIN;Nový zoznam PIN;Nova PIN lista;
;S21F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S21F270;Neue PIN Liste erzeugen.;Create new PIN list.;Créer une nouvelle liste de codes PIN;Crea nuovo elenco PIN.;Neue PIN Liste erzeugen.;Crear nueva lista de PIN.;Gerar nova lista PIN;Maak een nieuwe PIN lijst.;Créer une nouvelle liste de codes PIN;Maak een nieuwe PIN lijst.;Yeni bir PIN listesi oluşturun.;Wygeneruj nową listę PIN.;Generați o nouă listă PIN.;Neue PIN Liste erzeugen.;Créer une nouvelle liste de codes PIN;Crea nuovo elenco PIN.;Vygenerujte nový seznam PIN.;Vytvorte nový zoznam PIN.;Kreiraj novu listu PIN-ova;
;S21F217;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;
;S21F201;Achtung, alle PINs werden geändert. Sind Sie sicher?;Attention, all PINs will be changed. Are you sure?;Attention, toutes les PINs sont changeé. Êtes-vous sûre?;Attenzione, tutti i PIN verranno modificati. Sei sicuro?;Achtung, alle PINs werden geändert. Sind Sie sicher?;Atención, todos los PIN serán cambiados. ¿Está seguro?;Atenção, todos os PINs serão alterados. Tem certeza?;Let op, alle pincodes worden gewijzigd. Weet je het zeker?;Attention, toutes les PINs sont changeé. Êtes-vous sûre?;Let op, alle pincodes worden gewijzigd. Weet je het zeker?;Dikkat, tüm PIN'ler değiştirilecektir. Emin misin?;Uwaga, wszystkie PIN zostaną zmienione. Jesteś pewny?;Atenție, toate codurile PIN vor fi modificate. Esti sigur?;Achtung, alle PINs werden geändert. Sind Sie sicher?;Attention, toutes les PINs sont changeé. Êtes-vous sûre?;Attenzione, tutti i PIN verranno modificati. Sei sicuro?;Pozor, všechny PINy budou změněny. Jsi si jistá?;Pozor, všetky PINy budú zmenené. Si si istý?;Pažnja, svi PIN-ovi će biti promjenjeni. Da li želite nastaviti?;
;S21F205;Abbrechen;Abort;Avorter;Aborto;Abbrechen;Abortar;Abortar;Afbreken;Avorter;Afbreken;Iptal;Anulować;Avorta;Abbrechen;Avorter;Aborto;Přerušit;Prerušiť;Prekid;
;S22F203;Mindestbetrag;Minimum amount;Montant minimal;Quantità minima;Mindestbetrag;Monto minimo;Valor mínimo;Minimale hoeveelheid;Montant minimal;Minimale hoeveelheid;Minimum miktar;Stawka minimalna;Cantitatea minima;Mindestbetrag;Montant minimal;Quantità minima;Minimální množství;Minimálna suma;Minimalan iznos;
;S22F208;Preis pro Stunde;Price per hour;Prix par heure;Prezzo all'ora;Preis pro Stunde;Precio por hora;Preço por hora;Prijs per uur;Prix par heure;Prijs per uur;Saatlik fiyat;Cena za godzinę;Pret pe ora;Preis pro Stunde;Prix par heure;Prezzo all'ora;Cena za hodinu;Cena za hodinu;Cijena po satu;
;S22F201;Mehrwertsteuer %;VAT %;T.V.A. %;IVA %;Mehrwertsteuer %;IVA %;Imposto Valor Acr. %;BTW %;T.V.A. %;BTW %;Katma değer Vergisi %;Podatek obrotowy %;Taxa pe valoare adaugata %;Mehrwertsteuer %;T.V.A. %;IVA %;Daň z přidané hodnoty %;Daň z pridanej hodnoty %;PDV %;
;S22F204;Tarif 2;Tariff 2;Tarif 2;Tariffa 2;Tarif 2;Tarifa 2;Tarifa 2;Tarief 2;Tarif 2;Tarief 2;Tarife 2;Taryfa 2;Tariful 2;Tarif 2;Tarif 2;Tariffa 2;Tarif 2;Tarifa 2;Tarifa 2;
;S22F207;Höchstparkdauer;Maximum parking;Maximale de;Parcheggio;Höchstparkdauer;Estacionamiento;Tempo máx. estacion.;Maximaal parkeren;Maximale de;Maximaal parkeren;Maksimum park süresi;Maksymalny czas;Timp maxim de;Höchstparkdauer;Maximale de;Parcheggio;Maximální doba parkování;Maximálny čas parkovania;Maksimalno trajanje;
;S22F206;-;-;stationnement;massimo;-;máximo;-;-;stationnement;-;-;parkowania;parcare;-;stationnement;massimo;-;-;parkinga;
;S22F220;Münzen;Coins;Monnaie;Monete;Münzen;Monedas;Moedas;Munten;Monnaie;Munten;Madeni paralar;Monety;Monede;Münzen;Monnaie;Monete;Mince;Mince;Kovanice;
;S22F221;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QR kod;
;S22F222;Mü+QR;Co+QR;Mo+QR;Mo+QR;Mü+QR;Mo+QR;Mo+QR;Mu+QR;Mo+QR;Mu+QR;Ma+QR;Mo+QR;Mo+QR;Mü+QR;Mo+QR;Mo+QR;Mi+QR;Mi+QR;Ko+QR;
;S22F211;Tagesticket;Day ticket;Billet journalier;Biglietto giornaliero;Tagesticket;Boleto de un día;Bilhete diário;Dagkaart;Billet journalier;Dagkaart;Günlük bilet;Bilet dzienny;Bilet de zi;Tagesticket;Billet journalier;Biglietto giornaliero;Denní jízdenka;Denný lístok;Dnevna karta;
;S22F215;Stunden;Hours;Heures;Ore;Stunden;Horas;Horas;Uren;Heures;Uren;Saat;Godziny;Ore;Stunden;Heures;Ore;Hodin;Hodiny;Sati;
;S22F216;Minuten;Minutes;Minutes;Minuti;Minuten;Minutos;Minutos;Minuten;Minutes;Minuten;Dakika;Minuty;Minute;Minuten;Minutes;Minuti;Minut;Minút;Minute;
;S22F217;Nein;No;Non;No;Nein;No;Não;Nee;Non;Nee;Hayır;Nie;Nu;Nein;Non;No;Ne;Nie;Ne;
;S22F218;Ja;Yes;Oui;Sì;Ja;Sí;Sim;Ja;Oui;Ja;Evet;Tak;Da;Ja;Oui;Sì;Ano;Áno;Da;
;S22F233;Zahlungsweise;Payment mode;Mode de paier;Metodo di pagare;Zahlungsweise;Modo de pago;Forma de pagar;Betaalmethode;Mode de paier;Betaalmethode;Ödeme şekli;Metoda płatności;Metodă de plată;Zahlungsweise;Mode de paier;Metodo di pagare;Způsob platby;Spôsob platby;Način plaćanja;
;S22F240;Tabelle;Table;Tableau;Tavolo;Tabelle;Tabla;Mesa;Tabel;Tableau;Tabel;Tabel;Tabela;Tabel;Tabelle;Tableau;Tavolo;Tabulka;Tabuľka;Tabela;
;S22F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S23F220;Werkseinstellung bei Neustart;Factory setting on restart;Réglage d'usine au redémarrage;Impostazione di fabbrica al riavvio;Werkseinstellung bei Neustart;Configuración de fábrica al reiniciar;Config. de fábrica ao reiniciar;Fabrieksinstelling bij herstart;Réglage d'usine au redémarrage;Fabrieksinstelling bij herstart;Yeniden başlatmada fabrika ayarı;Ustawienie fabryczne przy restarcie;Resetarea din fabrică la repornire;Werkseinstellung bei Neustart;Réglage d'usine au redémarrage;Impostazione di fabbrica al riavvio;Tovární nastavení při restartu;Výrobné nastavenie pri reštarte;Vrati na tvorničke postavke prilikom ponovnog pokretanja;
;S23F203;Serialnumber;Serialnumber;Serialnumber;Numero di serie;Serialnumber;Número de serie;Número de série;Serienummer;Serialnumber;Serienummer;Seri numarası;Numer seryjny;Număr de serie;Serialnumber;Serialnumber;Numero di serie;Sériové číslo;Sériové číslo;Serijski broj;
;S23F202;Version;Version;Version;Versione;Version;Versión;Versão;Versie;Version;Versie;Versiyon;Wersja;Versiune;Version;Version;Versione;Verze;Verzia;Verzija;
;S23F204;Gerät;Device;Appareil;Dispositivo;Gerät;Dispositivo;Dispositivo;Apparaat;Appareil;Apparaat;Cihaz;Urządzenie;Dispozitiv;Gerät;Appareil;Dispositivo;Přístroj;Zariadenie;Uređaj;
;S23F205;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;
;S23F206;Screen;Screen;Screen;Schermo;Screen;Pantalla;Tela;Scherm;Screen;Scherm;Ekran;Ekran;Ecran;Screen;Screen;Schermo;Obrazovka;Obrazovke;Zaslon;
;S23F207;Speicher bereinigen;Clean up memory;Nettoyer la mémoire;Pulisci la memoria;Speicher bereinigen;Limpiar la memoria;Limpar a memória;Geheugen opschonen;Nettoyer la mémoire;Geheugen opschonen;Belleği temizle;Wyczyść pamięć;Curățați memoria;Speicher bereinigen;Nettoyer la mémoire;Pulisci la memoria;Vyčistěte paměť;Vyčistite pamäť;Izbrisati memoriju;
;S23F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S23F201;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Parče;
;S23F217;Nein;No;Non;No;Nein;No;Não;Nee;Non;Nee;Hayır;Nie;Nu;Nein;Non;No;Ne;Nie;Ne;
;S23F218;Ja;Yes;Oui;Sì;Ja;Sí;Sim;Ja;Oui;Ja;Evet;Tak;Da;Ja;Oui;Sì;Ano;Áno;Da;
;S26F270;Bitte SD Karte einlegen;Please insert SD card;Veuillez insérer la carte SD;Inserire la scheda SD;Bitte SD Karte einlegen;Inserte la tarjeta SD;Por favor insira cart. SD;Plaats a.u.b. SD-kaart;Veuillez insérer la carte SD;Plaats a.u.b. SD-kaart;Lütfen SD kartı takın;Proszę włożyć kartę SD;Vă rugăm să introduceți cardul SD;Bitte SD Karte einlegen;Veuillez insérer la carte SD;Inserire la scheda SD;Vložte prosím SD kartu;Vložte SD kartu;Umetnite SD karticu;
;PARKSCHEIN;Parkschein;Parkticket;Ticket de parking;Biglietto del parco;Parkschein;Boleto estacionamiento;Bilhete estacion.;Parkeerbon;Ticket de parking;Parkeerbon;Otopark bileti;Bilet parkingowy;Bilet de parcare;Parkschein;Ticket de parking;Biglietto del parco;Parkovací lístek;Parkovací lístok;Parkirna karta;
;DATUM;%d.%m.%y;%y/%m/%d;%d.%m.%y;%d.%m.%y;%d.%m.%y;%y/%m/%d;%d.%m.%y;%y/%m/%d;%d.%m.%y;%y/%m/%d;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;
;ZEIT;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;
;ZEITS;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;
;LCODE;DE;EN;FR;IT;DE;ES;PT;NL;FR;FL;TR;PL;RO;DE;FR;IT;CS;SK;HR;
;UST;UST;VAT;TVA;IVA;UST;IVA;IVA;BTW;TVA;BTW;VAT;VAT;VAT;UST;TVA;IVA;VAT;VAT;PDV;
;USTK;Umsatzst.;VAT;TVA;IVA;Umsatzst.;IVA;Imp. vend.;BTW;TVA;BTW;KDV;PTU;Taxa de vanzari;Umsatzst.;TVA;IVA;Prodejní daň;DPH;PDV;
;MINDESTENS;mind.;at least;au moins;almeno;mind.;al menos;no mín.;tenminste;au moins;tenminste;en azından;przynaj.;macar;mind.;au moins;almeno;alespoň;najmenej;najmanje;
;TAGESTICKET;Tagesticket ab;Dayticket from;Billiet jour de;Biglietto giornaliero da;Tagesticket ab;Billete de un día desde;Bilhete diário;Dagkaart vanaf;Billiet jour de;Dagkaart vanaf;Günlük bilet;Bilet dzienny od;Bilet de zi;Tagesticket ab;Billiet jour de;Biglietto giornaliero da;Denní jízdenka;Denný lístok;Dnevna karta od;
;TAG;Tag;day;jour;giorno;Tag;día;Dia;Dag;jour;Dag;Gün;Dzień;Zi;Tag;jour;giorno;Den;Deň;dan;
;TAGE;Tage;days;jours;giorni;Tage;días;Dias;Dagen;jours;Dagen;Günler;Dni;Zile;Tage;jours;giorni;Dní;Dni;dani;
;S27F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S27F5;QR Code scannen, TAN bekommen, eingeben;Scan QR code, get TAN, enter;Scan QR code, obtenir TAN, taper;Scansiona il codice QR, ottieni TAN, entra;QR Code scannen, TAN bekommen, eingeben;Escanee QR code, obtenga TAN, ingrese;Digitalize cód. QR, obtenha TAN, entre;Scan QR-code, ontvang TAN, voer in;Scan QR code, obtenir TAN, taper;Scan QR-code, ontvang TAN, voer in;QR kodunu tarayın, TAN alın, girin;Zeskanuj QR Code, uzyskaj TAN, wprowadź;Scanați QR Code, obțineți TAN, introduceți;QR Code scannen, TAN bekommen, eingeben;Scan QR code, obtenir TAN, taper;Scansiona il codice QR, ottieni TAN, entra;Naskenujte QR kód, získejte TAN, zadejte;Naskenujte QR kód, získajte TAN, zadajte;Skeniraj QR kod, uzmi TAN broj,pritisni enter=potvrda;
;S27F3;Ungültig;Invalid;Invalide;Non valido;Ungültig;Inválido;Inválido;Ongeldig;Invalide;Ongeldig;Geçersiz;Nieważny;Invalid;Ungültig;Invalide;Non valido;Neplatný;Neplatné;Nije valjano;
;S28F204;Tariftabelle;Tariff Table;Tableau tarifaire;Tabella tariffaria;Tariftabelle;Tabla de tarifas;Tabela tarifária;Tarieftabel;Tableau tarifaire;Tarieftabel;Tarife tablosu;Tabela taryfowa;Tabel tarifar;Tariftabelle;Tableau tarifaire;Tabella tariffaria;Tariff Table;Tariff Table;Tarifna tablica;
;S29F204;Tariftabelle;Tariff Table;Tableau tarifaire;Tabella tariffaria;Tariftabelle;Tabla de tarifas;Tabela tarifária;Tarieftabel;Tableau tarifaire;Tarieftabel;Tarife tablosu;Tabela taryfowa;Tabel tarifar;Tariftabelle;Tableau tarifaire;Tabella tariffaria;Tariff Table;Tariff Table;Tarifna tablica;
;S28F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S29F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S28F215;Stunden;Hours;Heures;Ore;Stunden;Horas;Horas;Uren;Heures;Uren;Saat;Godziny;Ore;Stunden;Heures;Ore;Hodin;Hodiny;Sati;
;S28F216;Preis;Price;Prix;Prezzo;Preis;Precio;Preço;Prijs;Prix;Prijs;Fiyat;Cena;Preț;Preis;Prix;Prezzo;Cena;Cena;Cijena;
;S28F217;Stunden;Hours;Heures;Ore;Stunden;Horas;Horas;Uren;Heures;Uren;Saat;Godziny;Ore;Stunden;Heures;Ore;Hodin;Hodiny;Sati;
;S28F218;Preis;Price;Prix;Prezzo;Preis;Precio;Preço;Prijs;Prix;Prijs;Fiyat;Cena;Preț;Preis;Prix;Prezzo;Cena;Cena;Cijena;
;S29F215;Stunden;Hours;Heures;Ore;Stunden;Horas;Horas;Uren;Heures;Uren;Saat;Godziny;Ore;Stunden;Heures;Ore;Hodin;Hodiny;Sati;
;S29F216;Preis;Price;Prix;Prezzo;Preis;Precio;Preço;Prijs;Prix;Prijs;Fiyat;Cena;Preț;Preis;Prix;Prezzo;Cena;Cena;Cijena;
;S29F217;Stunden;Hours;Heures;Ore;Stunden;Horas;Horas;Uren;Heures;Uren;Saat;Godziny;Ore;Stunden;Heures;Ore;Hodin;Hodiny;Sati;
;S29F218;Preis;Price;Prix;Prezzo;Preis;Precio;Preço;Prijs;Prix;Prijs;Fiyat;Cena;Preț;Preis;Prix;Prezzo;Cena;Cena;Cijena;
;PROSTD;pro Stunde;per hour;par heure;all'ora;pro Stunde;por hora;por hora;per uur;par heure;per uur;saat başı;na godzinę;pe ora;pro Stunde;par heure;all'ora;za hodinu;za hodinu;po satu;
;DAVON;davon;from that;de la;da quello;davon;a partir de ese;a partir desse;van dat;de la;van dat;Bundan;z tego;din aceasta;davon;de la;da quello;od toho;odtiaľto;iz toga;
;PARKSCHEINVOM;Parkschein vom;Parkticket from;Ticket de parking de;Biglietto da;Parkschein vom;Boleto estacionamiento;Bilhete estacion. de;Parkeerbon vanaf;Ticket de parking de;Parkeerbon vanaf;Park bileti tarihi;Bilet parkingowy od;Bilet de parcare de la;Parkschein vom;Ticket de parking de;Biglietto da;Parkovací lístek od;Parkovací lístok od;Parkirna karta iz;
;GUTSCHEINVOM;Gutschein vom;Voucher from;Bon d achat de;Buono da;Gutschein vom;Vale de;Voucher de;Waardebon van;Bon d achat de;Waardebon van;Kupon tarihi;Kupon od;Voucher de la;Gutschein vom;Bon d achat de;Buono da;Poukaz od;Poukaz od;Vaučer iz;
;PARKDAUER;Parkdauer;Parking duration;Duree de parking;Durata del parcheggio;Parkdauer;Duración del estacionamiento;Duração do estacion.;Parkeerduur;Duree de parking;Parkeerduur;Park süresi;Czas parkowania;Durata de parcare;Parkdauer;Duree de parking;Durata del parcheggio;Doba parkování;Trvanie parkovania;Duljina parkiranja;
;PARKENBIS;Parken bis;Parking until;Parking d ici a;Parcheggio fino al;Parken bis;Estacionamiento hasta;Estacion. até;Parkeren tot;Parking d ici a;Parkeren tot;Kadar park etmek;Parkowanie do;Parcare până la;Parken bis;Parking d ici a;Parcheggio fino al;Parkování do;Parkovanie do;Parkiranje do;
;GUELTIGBIS;Gueltig bis;Valid until;Valable jusqu a;Valido fino a;Gueltig bis;Válido hasta;Data de Expiração;Geldig tot;Valable jusqu a;Geldig tot;Kadar geçerli;Ważny do;Data expirarii;Gueltig bis;Valable jusqu a;Valido fino a;Datum spotřeby;Dátum spotreby;Valjano do;
;STD;Std.;h;h;h;Std.;h;H.;h;h;h;Sa.;Godziny;Ore;Std.;h;h;h.;H.;sat;
;MIN;Min.;min;min;min;Min.;min;Min.;min;min;min;Dk.;Minuty;Minute;Min.;min;min;m.;Min.;min;
;BARZAHLUNG;Barzahlung;Cash payment;Payment comptant;Pagamento in contanti;Barzahlung;Pago en efectivo;Pagamento em dinheiro;Contante betaling;Payment comptant;Contante betaling;Nakit ödeme;Zapłata gotówką;Plată în numerar;Barzahlung;Payment comptant;Pagamento in contanti;Platba v hotovosti;Platba v hotovosti;Gotovinsko plaćanje;
;BARGELDLOS;bargeldlos;cashless;sans argent liqu;senza soldi;bargeldlos;sin efectivo;sem dinheiro;zonder contant geld;sans argent liqu;zonder contant geld;Nakitsiz;bezgotówkowy;fără numerar;bargeldlos;sans argent liqu;senza soldi;Bezhotovostně;bezhotovostne;bezgotovinsko;
;NOTVALID1;Nicht signierter Ausgabereport.;Informal report, not signed.;Informal report, not signed.;Informal report, not signed.;Nicht signierter Ausgabereport.;Informe informal, no firmado.;Relatório de saída não assinado.;Informeel rapport, niet;Informal report, not signed.;Informeel rapport, niet;İmzasız çıktı raporu.;Niepodpisany raport wyjściowy.;Raport de ieșire nesemnat.;Nicht signierter Ausgabereport.;Informal report, not signed.;Informal report, not signed.;Nepodepsaná výstupní sestava.;Nepodpísaný protokol.;Nepotpisano izvješće o izlazu.;
;NOTVALID2;Nur nachrichtliche Werte.;Only for information.;Only for information.;Only for information.;Nur nachrichtliche Werte.;Solo para información.;Valores apenas informativos.;ondertekend. Alleen ter informatie.;Only for information.;ondertekend. Alleen ter informatie.;Yalnızca bilgi değerleri.;Tylko wartości informacyjne.;Numai valori informative.;Nur nachrichtliche Werte.;Only for information.;Only for information.;Pouze informativní hodnoty.;Len informatívne hodnoty.;Samo informacijske vrijednosti.;
;NOTVALID3;Kann nicht als Umsatznachweis;Not usable as a valid;Not usable as a valid;Not usable as a valid;Kann nicht als Umsatznachweis;No se puede utilizar como una;Não pode ser usado como prova;informatie. Niet bruikbaar als;Not usable as a valid;informatie. Niet bruikbaar als;Vergi makamları için satış;Nie może służyć jako dowód;Nu poate fi folosit ca dovadă;Kann nicht als Umsatznachweis;Not usable as a valid;Not usable as a valid;Nelze použít jako doklad;Nemožno použiť ako doklad;Ne može se koristiti kao;
;NOTVALID4;fuer die Steuerbehoerden;revenue statement for;revenue statement for;revenue statement for;fuer die Steuerbehoerden;declaración de ingresos válida;de venda para as;geldig inkomstenoverzicht voor;revenue statement for;geldig inkomstenoverzicht voor;kanıtı olarak kullanılamaz.;sprzedaży dla;de vânzare pentru;fuer die Steuerbehoerden;revenue statement for;revenue statement for;o prodeji pro;o predaji pre;izvješće o prodaji za;
;NOTVALID5;verwendet werden.;tax authorities.;tax authorities.;tax authorities.;verwendet werden.;para las autoridades fiscales.;autoridades fiscais.;de Belastingdienst.;tax authorities.;de Belastingdienst.;-;organów podatkowych.;autoritățile fiscale.;verwendet werden.;tax authorities.;tax authorities.;finanční úřady.;daňové úrady.;porezne vlasti.;
;USTREPORT;USt;VAT;TVA;IVA;USt;IVA;IVA;BTW;TVA;BTW;KDV;PTU;TVA;USt;TVA;IVA;DPH;DPH;PDV;
;PROTOKOLLTITEL;Protokoll verkaufte Parkscheine;Protocol sold parking tickets;Protocole des tickets vendus;Protocollo biglietti venduti;Protokoll verkaufte Parkscheine;Protocolo de billetes de estacionamiento vendidos;Protoc. bilhetes estacion. vend.;Protocol verkochte parkeerbonnen;Protocole des tickets vendus;Protocol verkochte parkeerbonnen;Protokol satılan park biletleri.;Rejestr sprzedanych bilet. park.;Protocol vândut bilete d parcare;Protokoll verkaufte Parkscheine;Protocole des tickets vendus;Protocollo biglietti venduti;Protokol prodával parkovací lístky;Protokol preda. parkov. lístkov;Prodane minute perkirnih karata;
;PROTOKOLLNR;Protokoll-Nr.;Protocol no.;Protocole no;Protocollo no.;Protokoll-Nr.;Protocolo no.;Protocolo nº;Protocol nr.;Protocole no;Protocol nr.;Protokol num.;Protokół nr.;Protocolul nr.;Protokoll-Nr.;Protocole no;Protocollo no.;Protokol č.;Protokol č.;Protokol br.;
;SIGNATUR;Signatur;Signature;Signature;Segnatura;Signatur;Signature;Assinat.;Signatuur;Signature;Signatuur;Imza;Sygnatura;Semnătură;Signatur;Signature;Segnatura;Podpis;Podpis;Potpis;
;ERZEUGT;erzeugt;generated;généré;generato;erzeugt;generado;gerado;gegenereerd;généré;gegenereerd;oluşturulan;wygenerowany;generat;erzeugt;généré;generato;vygenerován;vygenerovaný;generirano;
;BITTETICKETSLOTA;Bitte Ticketslot bestellen;Please order ticketslot;Please order ticketslot;Please order ticketslot;Bitte Ticketslot bestellen;Please order ticketslot;Please order ticketslot;Please order ticketslot;Please order ticketslot;Please order ticketslot;Please order ticketslot;Please order ticketslot;Please order ticketslot;Bitte Ticketslot bestellen;Please order ticketslot;Please order ticketslot;Please order ticketslot;Please order ticketslot;Molimo naručite ticketslot;
;BITTETICKETSLOTB;durch Ueberweisung unter;by wiring under;by wiring under;by wiring under;durch Ueberweisung unter;by wiring under;by wiring under;by wiring under;by wiring under;by wiring under;by wiring under;by wiring under;by wiring under;durch Ueberweisung unter;by wiring under;by wiring under;by wiring under;by wiring under;bankovnim transferom;
;BITTETICKETSLOTC;Angabe einer oder mehrerer;specification of one or;specification of one or;specification of one or;Angabe einer oder mehrerer;specification of one or;specification of one or;specification of one or;specification of one or;specification of one or;specification of one or;specification of one or;specification of one or;Angabe einer oder mehrerer;specification of one or;specification of one or;specification of one or;specification of one or;navodeći jedan ili više brojeva;
;BITTETICKETSLOTD;Ordernummern;more order numbers;more order numbers;more order numbers;Ordernummern;more order numbers;more order numbers;more order numbers;more order numbers;more order numbers;more order numbers;more order numbers;more order numbers;Ordernummern;more order numbers;more order numbers;more order numbers;more order numbers;brojeva narudžbe;
;BANKVERBINDUNG;Bankverbindung siehe unter:;Bank account see under:;Bank account see under:;Bank account see under:;Bankverbindung siehe unter:;Bank account see under:;Bank account see under:;Bank account see under:;Bank account see under:;Bank account see under:;Bank account see under:;Bank account see under:;Bank account see under:;Bankverbindung siehe unter:;Bank account see under:;Bank account see under:;Bank account see under:;Bank account see under:;Bankovne podatke vidi pod:;
;SPEICHERSIGNIERUNG1;Speichersignierung anfordern;Request for storage signing;Request for storage signing;Request for storage signing;Speichersignierung anfordern;Request for storage signing;Request for storage signing;Request for storage signing;Request for storage signing;Request for storage signing;Request for storage signing;Request for storage signing;Request for storage signing;Speichersignierung anfordern;Request for storage signing;Request for storage signing;Request for storage signing;Request for storage signing;Zatražite memorijski potpis;
;SPEICHERSIGNIERUNG2;durch Angabe der Ordernummer;by specification of the ordernumber;by specification of the ordernumber;by specification of the ordernumber;durch Angabe der Ordernummer;by specification of the ordernumber;by specification of the ordernumber;by specification of the ordernumber;by specification of the ordernumber;by specification of the ordernumber;by specification of the ordernumber;by specification of the ordernumber;by specification of the ordernumber;durch Angabe der Ordernummer;by specification of the ordernumber;by specification of the ordernumber;by specification of the ordernumber;by specification of the ordernumber;navodeći broj naloga;
;SPEICHERSIGNIERUNG3;Details siehe unter:;Details see under:;Details see under:;Details see under:;Details siehe unter:;Details see under:;Details see under:;Details see under:;Details see under:;Details see under:;Details see under:;Details see under:;Details see under:;Details siehe unter:;Details see under:;Details see under:;Details see under:;Details see under:;Pojedinosti vidi pod:;
;VON;von;by;de;da;von;de;de;van;de;van;ile;od;din;von;de;da;od;od;od;
;BATTERYA;Bitte Batteriestatus;Please check;Veuillez vérifier l'état de;Si prega di controllare lo stato;Bitte Batteriestatus;Por favor, compruebe el estado;Por favor, verifique o estado;Controleer de;Veuillez vérifier l'état de;Controleer de;Lütfen pil durumunu;Sprawdź stan;Vă rugăm să verificați;Bitte Batteriestatus;Veuillez vérifier l'état de;Si prega di controllare lo stato;Zkontrolujte prosím;Skontrolujte stav;Provjerite stanje;
;BATTERYB;ueberpruefen;battery state;la batterie;della batteria;ueberpruefen;de la batería;da bateria;batterijstatus;la batterie;batterijstatus;kontrol edin;baterii;starea bateriei;ueberpruefen;la batterie;della batteria;stav baterie;batérie;baterije;
;BATTERYC;Dann neu starten;Then restart;Redémarrez ensuite l'appareil;Dopo quel riavvio;Dann neu starten;Después de ese reinicio;Em seguida, reinicie;Daarna opnieuw opstarten;Redémarrez ensuite l'appareil;Daarna opnieuw opstarten;Bundan sonra yeniden başlatın;Po tym restarcie;După această repornire;Dann neu starten;Redémarrez ensuite l'appareil;Dopo quel riavvio;Po tomto restartu;Po tomto reštarte;Nakon ponovnog pokretanja;
]]
--************************************************************************
--private
if arg ~= nil and arg[1] == 'test7' then
function Field.write(self) return nil end
function start_timer() return nil end
local x = App:new(1)
t(2,"DE")
x.report_data = {}
x:test1_generate_report(arg[2])
end
--************************************************************************
--private
if arg ~= nil and arg[1] == 'test75' then
local x = App:new(1)
local x1 = base32_encode(arg[2])
local x2 = base32_decode(x1)
print(arg[2])
print(x1)
print(x2)
print(arg[2])
end
--************************************************************************
--private
if arg ~= nil and arg[1] == 'test701' then
local x = App:new(1)
-- local text1 = "3c9bcf7b223e894984cc7bd7cd8871752889284a7b7c9ebe330167fc57c72a1d083687f56b21826bddda46ee4021c6174b89155a3b9b19b0dbcc6dfdea193391bd4d3ea0af33ee5b7f86df3d5c45462f05aa2f92b554ac2891cf3f34e10a37654626ba0a6dad93c858c6354c8b1d5491962b63c080bb226222"
-- local text2 = base62.decode(text1)
-- print(text2)
-- local text3 = bin.stohex(text2)
-- print(text3)
-- print("qqq4",text1_crypt)
local text33 = "3c9bcf7b223e894984cc7bd7cd8871752889284a7b7c9ebe330167fc57c72a1d083687f56b21826bddda46ee4021c6174b89155a3b9b19b0dbcc6dfdea193391bd4d3ea0af33ee5b7f86df3d5c45462f05aa2f92b554ac2891cf3f34e10a37654626ba0a6dad93c858c6354c8b1d5491962b63c080bb226222"
local text3 = "6f4a57ada0dc62baf753dc34b4bc5f75288ab5477b7c9ebe33f119565742e9c8108e22c75f359162fd5b4a29cacf7ccadb1e430f3b5779ea0c9e2342f86d5d28b87135ed1f3de8ee0e1267751dbf20f83f3fea463c9a602b962d113a4e1231fe717303740e83094c5907646da7e64c95c330b0d522ba797733a6887ce0027bbe476a0dd25834"
local text1_bin = x:decrypt(pub_key, text3 )
print(text1_bin)
-- local text11 = bin.stohex( text1_bin )
-- print("qqq7",text11)
end
--************************************************************************
--private
if arg ~= nil and arg[1] == 'test702' then
local x = App:new(1)
-- local text1 = "3c9bcf7b223e894984cc7bd7cd8871752889284a7b7c9ebe330167fc57c72a1d083687f56b21826bddda46ee4021c6174b89155a3b9b19b0dbcc6dfdea193391bd4d3ea0af33ee5b7f86df3d5c45462f05aa2f92b554ac2891cf3f34e10a37654626ba0a6dad93c858c6354c8b1d5491962b63c080bb226222"
-- local text2 = base62.decode(text1)
-- print(text2)
-- local text3 = bin.stohex(text2)
-- print(text3)
-- print("qqq4",text1_crypt)
local text33 = "3c9bcf7b223e894984cc7bd7cd8871752889284a7b7c9ebe330167fc57c72a1d083687f56b21826bddda46ee4021c6174b89155a3b9b19b0dbcc6dfdea193391bd4d3ea0af33ee5b7f86df3d5c45462f05aa2f92b554ac2891cf3f34e10a37654626ba0a6dad93c858c6354c8b1d5491962b63c080bb226222"
local text3 = "6f4a57ada0dc62baf753dc34b4bc5f75288ab5477b7c9ebe33f119565742e9c8108e22c75f359162fd5b4a29cacf7ccadb1e430f3b5779ea0c9e2342f86d5d28b87135ed1f3de8ee0e1267751dbf20f83f3fea463c9a602b962d113a4e1231fe717303740e83094c5907646da7e64c95c330b0d522ba797733a6887ce0027bbe476a0dd25834"
local text1_bin = x:decrypt(pub_key, text3 )
print(text1_bin)
-- local text11 = bin.stohex( text1_bin )
-- print("qqq7",text11)
end
--************************************************************************
--private
if arg ~= nil and arg[1] == 'test703' then
local x = App:new(1)
-- local x0 = "ffffffff444455556666777788884444ffffeeee44445555666677778888ffff" print(x0)
local x0 = "fedc1" print(x0)
local x1 = base27precise.encode(x0) print(x1)
local x2 = base27precise.decode(x1) print(x2)
-- print(#x0)
-- print(#x1)
-- print(#x2)
end
--************************************************************************
--private
if arg ~= nil and arg[1] == 'test801' then
local x = App:new(1)
-- local x0 = "ffffffff444455556666777788884444ffffeeee44445555666677778888ffff" print(x0)
function stop_timer() return nil end
function start_timer() return nil end
x:zeitumstellung()
-- print(#x0)
-- print(#x1)
-- print(#x2)
end
--************************************************************************
if arg ~= nil and arg[1] == 'fcheck' then
a = fcheck(-99,tonumber,"00a")
print(a)
end