
| Current Path : /var/www/web-klick.de/dsh/ |
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 : /var/www/web-klick.de/dsh/ii |
--*****************************************************************************
SERIALNUMBER = '0432779'
GIT_COMMIT = '0.99c (63870c9a)'
FIRMWARE_VER = 'V_16.7_edison'
POINTS_PER_EURO = 0.8 -- 1.0: 6,33 Prozent, 0.8: 5,01 Prozent (190/3000*100*PPE)
HAS_KNOB = 1
LANGUAGE_FILTER = "DE,EN,UK,FR,ES,IT"
LANGUAGE_FILTER = "DE,EN,UK,FR"
COINVALUE = { 0.00, 0.10, 0.20, 0.50, 1.00, 2.00 }
CURRENT = 'EUR'
sec_key = 'a9a458b5db438cf76f40229190b0a5f0cb5e940f17fa3e3e8dbf767c01513a6ac1ec2eaf127eacd2c872df863b0b00234067bce2459977e96f5a9d3ff6b6dbec'
pub_key = '12d7b72dd7db8283dc0748e587c85158cee1a9c1c100cc09e8556a79e8cc53593b731c6cc6940c118fda320b73acf3cbb11b235955937f1ca2c1d80d22d770bd'
ak1 = '3b731c6cc6940c118fda320b73acf3cbb11b235955937f1ca2c1d80d22d770bd'
ak2 = 'c1ec2eaf127eacd2c872df863b0b00234067bce2459977e96f5a9d3ff6b6dbec'
-- Parkticket Firmware
-- vgabriel / cgabriel
-- Copyright 31.01.2021 IfT GmbH
---PATCH---
RESET_FLASH = 0
DISABLED = 0
sec_key1 = sec_key -- Asymmetrische Signierung des Flash
pub_key1 = pub_key -- mit EC 25519
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) --
--*****************************************************************************
-- CORE_END
--*****************************************************************************
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)
local days = (year-1970)*365 + math.floor((year-1970)/4)
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 = '1375629804'
local function encode(s)
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)
-- 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 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)
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 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)
-- 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)
end --decode()
return { -- base27 module
encode = encode,
decode = decode,
}
end
base27 = preload_1008a()
--*****************************************************************************
-- CORE_START
--*****************************************************************************
--core
function preload_1001()
function preload_1002()
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)
local st = salsa20_block(key, counter, nonce)
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_1002()
function preload_1003()
------------------------------------------------------------
-- 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%12 == 0 and file_open ~= nil then
start_timer(26,WAIT_26,0,1)
coroutine.yield()
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)
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)
sel25519(a,b,r)
sel25519(c,d,r)
end
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
inv25519(x32,x32)
M(x16,x16,x32)
pack25519(q,x16)
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_1003()
function preload_1004()
-----------------------------------------------------------
-- 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_1004()
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 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 == 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)
-- 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)
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( tonumber1(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,
}
end
bin = preload_1009()
--*************************************************************************
-- MAIN ROUTINES
--*************************************************************************
--core
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 = {}
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
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
--**********************************************************************
--core
function on_control_notify (sid,control_nr,val)
return app:on_control_notify(sid,control_nr,val)
end
--**********************************************************************
--core
function on_screen_change (sid)
if app ~= nil then
-- print('ID',sid)
app:on_screen_change(sid)
end
end
--**********************************************************************
--core
function on_uart_recv_data(packet)
app:on_uart_recv_data(packet,"comes from real hardware")
end
--**********************************************************************
--core
function on_timer (tid)
if tid == 11 then
load_patch(PATCH_filename)
elseif tid == 12 then
load_parts()
else
return app:on_timer_pre(tid)
end
end
--**********************************************************************
--core
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
local x = string.find(translation_table,"\n%C-;"..expression..";")
if x == nil then
return expression
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
--**********************************************************************
--core
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
--**********************************************************************
--core
--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
--**********************************************************************
--core
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
--**********************************************************************
--core
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)
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! ! !')
end
--关闭文件
file_close()
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
--**********************************************************************
--core
function feed_the_dog ()
if file_open ~= nil then
feed_dog()
end
end
--**********************************************************************
--core
function on_list_dir (path,filename)
feed_the_dog()
table.insert(sd_subdirs,filename)
if string.sub(filename,1,5) == 'patch' then
PATCH_filename = filename
start_timer(11,500,0,1)
ladebalken = 5
set_text(25,5,ladebalken)
end
end
--**********************************************************************
--core
function load_patch (filename)
stop_timer(10)
ladebalken = ladebalken + 5
set_text(25,1,ladebalken)
patch_file = filename
local patch_enc = ""
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
PATCH_plaintext = ''
PATCH_parts = 0
PATCH_zaehler = 0
PATCH_fortschritt = 1
PATCH_nr = 0
PATCH_zeilen = {}
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 = tonumber(string.sub(zeile,3))
else
local chunk = ""
if #ciph > 20 then
chunk = App.decrypt('',sec_key1,trim(ciph))
end
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
local SAVE_sd_dir = sd_dir
local SAVE_sd_subdirs = sd_subdirs
patch_call()
PATCH_plaintext = ""
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
--*********************************************************************
--core
function trim (text)
return (text:gsub('^%s*(.-)%s*$', '%1'))
end
--*************************************************************************
-- CLASSES
--*************************************************************************
App = {}
App.__index = App
--*************************************************************************
--core
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
start_timer(10,1,0,1)
else
if App.init ~= nil then
self:init()
end
end
return self
end
--**********************************************************************
--core
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
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
--**********************************************************************
--core
function App.decrypt (self,ckey,text)
if text == nil then
return ''
end
text = bin.hextos(text)
if #text < 16 then
return('')
end
local erg = ''
if #ckey == 32 then
local ckey1 = bin.hextos(ckey)
erg = rc4.decrypt(ckey1,text)
elseif #ckey == 64 then
local ckey1 = bin.hextos(ckey)
erg = salsa20.decrypt(ckey1,1,bin.hextos('1234567890abcdef'),text)
else
local ckey1 = bin.hextos( string.sub(ckey,1,64) )
local ckey2 = bin.hextos( string.sub(ckey,65) )
erg = crypto.box_open(text,string.sub(SERIALNUMBER..'00000000000000000000',1,24),
ckey1,ckey2)
end
return erg
end
--**********************************************************************
-- CORE_END
--**********************************************************************
function App.crypt (self,ckey,text)
if text == nil then
return ''
end
local erg = ''
if #ckey == 32 then
local ckey1 = bin.hextos(ckey)
erg = rc4.encrypt(ckey1,text)
elseif #ckey == 64 then
local ckey1 = bin.hextos(ckey)
erg = salsa20.encrypt(ckey1,1,bin.hextos('1234567890abcdef'),text)
else
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
erg = bin.stohex(erg)
return erg
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.counter = nil
-- self:check_for_flash_reset("341674")
self:no_simulation()
end
CHECK_SIGN = "sign_" .. string.sub("%4u",self:time()) .. ".txt"
self:init1()
end
--**********************************************************************
function App.init1 (self)
-- self.uart_sperre = 0
self.field_sim = {}
self.screens = {}
self.fields = {}
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.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, } )
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, } )
self.adm_proto = Screen:new(self, 9, { 213, 205, 299, 203, 202, } )
self.not_ready = Screen:new(self,10, { 270, } )
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, } )
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, } )
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, { 215, 216, 217, 218, 299 } )
self.adm_tarif_def2 = Screen:new(self,29, { 215, 216, 217, 218, 299 } )
self.debug1 = Field:new(self,'')
self.debug1:add_field(self.standby,200)
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)
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,'1.50'), Field:new(self,'0.50') }
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,'0.50'), Field:new(self,'0.00') }
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,'5'), Field:new(self,'5') }
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,'00'), Field:new(self,'00') }
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,'0'), Field:new(self,'0') } -- 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,'2'), Field:new(self,'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,'19')
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,'50')
self.max_zeilen:add_field(self.adm_proto,5)
self.max_zeilen:add_flash_position(280,10)
self.reportnumber = Field:new(self,'0')
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,'12park')
self.location1:add_field(self.adm_basic,14)
self.location1:add_flash_position(140,10)
self.location2 = Field:new(self,'12park.de, 0911/148781-45')
self.location2:add_field(self.adm_basic,10)
self.location2:add_flash_position(180,10)
self.location3 = Field:new(self,'www.12park.de')
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)
self.country_table = {"DE","UK","FR","AT","CH","CZ","DK","ES","FI","GR","HU","IE","IT","NL","NO","PL","RO","RU","SE","TR"}
self.ldescriptions = {"Deutsch","English","Français","Deutsch","Deutsch","Čeština","Dansk","Español","Suomalainen",
"Ελληνικά","Magyar","Gaeilge","Italiano","Nederlands","Norsk","Polskie","Română","Русский",
"Svenska","Türk"}
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
self.adm_lang_sel:set_text(20+#first_three_languages/2,lang)
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.ticketslot_pin = Field:new(self,'', self.ticketslot,2)
self.pin_start = Field:new(self,'',self.pin_adm_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)
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,'')
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.pin_wartezeit = 0
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
self:init3()
end
--**********************************************************************
function App.init3 (self)
self.tarif_par = { {}, {}, {}, {} }
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
self:make_tarif_table()
self:init4()
end
--**********************************************************************
function App.init4 (self)
self.signature_positions = {}
for i = 0,9 do
table.insert(self.signature_positions,12300+i*4096)
end
self.counter = {}
for i = 0,9,1 do
local count = Field:new(self,'100')
if file_open == nil then
count = Field:new(self,'10000')
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:init5()
end
--**********************************************************************
function App.init5 (self)
if file_open ~= nil then
start_timer(29,60000,0,1)
end
self.TIMEOUT = 18
self.REDUCE_BACKLIGHT = 14
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
if file_open == nil then
self.LOW_BACKLIGHT = 70
self.BLACKSCREEN = 68
end
self.pin_randomseed = Field:new(self, SERIALNUMBER )
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
-- 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.ser_number:write(SERIALNUMBER)
if GIT_COMMIT0 == nil then
self.git_version:write(GIT_COMMIT)
self.patchv:write( 'none' )
else
self.git_version:write(GIT_COMMIT0)
self.patchv:write( GIT_COMMIT )
end
self.firmware:write(FIRMWARE_VER)
self.screen_nr:write( get_version() )
self.used_pins = {}
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()
self.debug1:set_visiable(0) -- @@ test
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
self:check_for_flash_reset('341674')
self:disable_device()
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)
self.no_sdcard:activate() -- keine SD Karte eingelegt
return
end
self.there_is_no_sdcard = 0
self:compute_shortinfo()
if self.simulation == 0 then
self:no_simulation()
end
local rep = self:read_report()
self:write_report(rep)
self.request_id = 0
self.flash_is_not_signed = 1 - self:check_flash_signature()
self.ticketslot_start = 0
if 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
self.ticketslot:activate()
-- self.adm_proto:activate() -- zu Testzwecken
else
self:update_counter(0)
self.no_timeout = 0
set_backlight(self.BLACKSCREEN)
self.standby:activate()
end
-- 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')
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.date (self,format_string,sec)
if self.simulation == 1 then
return os.date(format_string,sec)
end
if sec == nil then
sec = self:time()
end
if format_string == nil then
return sec
end
if sec > 1000000000000 then
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
else
return date1.date_format(format_string,sec)
end
end
--**********************************************************************
function App.time (self,tt)
if self.simulation == 1 then
return os.time()
end
local year = ''
local month = ''
local day = ''
local hour = ''
local min = ''
local sec = ''
local week = ''
year, month, day, hour, min, sec, week = get_date_time()
-- hour = hour - 12 @@ zu Testzwecken
local sec = date1.date_reverse(year,month,day,hour,min,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")
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')
for zaehler = 0,31,1 do
feed_the_dog()
write_flash_string(1+zaehler*2048, string.rep(' ',2040))
end
change_screen(13)
return
end
end
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()
table.insert(contents,{ nr, tt })
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
-- if file_open ~= nil then
-- my_write_filedata(sd_dir.."/"..CHECK_SIGN,text,over_write)
-- end
local md5key = bin.stohex( md5lib.hash(text) )
local ciph = self:crypt(sec_key3,md5key)
-- 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 then
-- print("CIPH",ciph)
for i,sign_pos in ipairs(self.signature_positions) do -- alle moeglichen Signatur-Positionen durchpruefen
if read_flash_string(sign_pos) == ciph then
return 1
end
end
return 0
end
print('SIGN FLASH')
local sign_idx = string.gsub(md5key,"%D","").."1"
self.aktueller_teilcounter = tonumber( string.sub(sign_idx,1,15) % #self.counter)
sign_idx = tonumber( string.sub(sign_idx,1,20) % #self.signature_positions)
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)
-- 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)
local ciph = self:crypt(sec_key1,pin)
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)
else
tan1 = math.ceil(10^11 + 1.44 * tan1 + 1234)
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 then
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.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
end
--**********************************************************************
function App.request_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 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,t('www.12park.de/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@')
self:send_to_printer(orderinfo)
end
--**********************************************************************
function App.send_to_printer (self,printdata)
self.print_data = printdata
start_timer(23,10,0,1)
end
--**********************************************************************
function App.send_to_printer1 (self,text,datei)
if file_open == nil then
if datei == nil then
datei = 'null.txt'
end
-- os.execute('echo '..parkticket..' > /tmp/dbg.txt')
io.open(datei,'w')
-- print('FILE','--------------------------------',f)
if f ~= nil then
for i,zeile in ipairs(text) do
zeile = zeile:gsub('QQ','\\\\')
-- f:write('echo -e ''..zeile..'' > /dev/usb/lp0\n')
end
f:close()
end
elseif true then
for i,zeile in ipairs(text) do
-- zeile = zeile:gsub('QQ','\\') .. '\n'
if string.sub(zeile,1,9) ~= '---DEL---' then
zeile = zeile .. '\r\n'
uart_send_data( self:str_to_byteArray(zeile))
end
if i % 50 == 0 then
feed_the_dog()
end
end
-- start_timer(6,1000,0,1)
end
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)
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
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
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.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()
end
elseif screen == self.ticketslot and control == 17 then
if self.ticketslot_start == 0 then
self.adm_ext:activate()
else
self:disable_device()
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.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:compute_shortinfo()
set_backlight(self.BLACKSCREEN)
self.standby:activate()
elseif screen == self.adm_lang_sel then
if control == 99 then
self.adm_base:activate()
else
self:sprachauswahl(control)
end
elseif screen == self.adm_ext and control == 12 then
self:pin_base(control)
elseif screen == self.adm_ext and control == 3 then
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
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
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: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.price_per_hour[1]:read()
local ttt = self.tagesticket[1]:read()
local dauer = self.max_park_hour[1]:read()
local d_min = string.format('%02u',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)
else
self.pin:activate()
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(cc1,cc2,cc3)
for lang,lfield in pairs(self.lang1) do
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)
if self.used_pins[self.aktuelle_pin_eingabe] == math.ceil(tonumber(self:time())/86400) then
erg = nil -- ist an diesemTag schon einmal aufgerufen worden
end
-- print('ERG',erg)
stop_timer(5)
self.ungueltig = self.ungueltig + 1
if self.pin_mode == 'lang' then
if erg == 'E' 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.aktuelle_pin_eingabe = ''
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('***********')
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('%H:%M')))
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
local pin_defs = self:read_pin_defs()
local x1,x2 = string.find(pin_defs,',.'..pin..'.-,')
if x1 ~= nil then
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
return mode
end
x1,x2 = string.find(pin_defs,',A')
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') -- Suche nach Admin-Passwoerten
if x1 == nil or math.random(1,4) == 1 then -- wenn es schon gesetzte Admin Passwoerter in der
return 'S' -- PIN-Verwaltung gibt, dann muss man mehrfach versuchen mit dem Standard-Passwort
end
end
end
--**********************************************************************
function App.sprachauswahl (self,control)
local sprache = self.adm_lang_sel:get_text(control+20)
-- print('Sprache',sprache,self.einzustellende_sprache)
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
self.lang_selector:write(langs)
end
self:switch_language(1)
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
-- 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 = 1
for i,lang in ipairs(self.country_table) do
if lang == langcode then
break
end
actual_lang_nr = actual_lang_nr + 1
end
return actual_lang_nr or -1
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:insert_coin(nr)
end
end
return
end
self.coin:activate()
self.nochmal:set_visiable(-1)
self.nochmal_button:set_visiable(-1)
-- print(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)
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 = string.format('%5.2f',self.amount_in_euro * self.mwst / (100 + self.mwst)):gsub('%.',',')
-- print('MWST',self.tax_vat:read(),mwst,self.umsatzsteuer)
local akt_time = self:time() + 45
local parkdauer_in_sec = self:compute_parkdauer()
local hoechstparkdauer = tonumber(self.max_park_hour[self.tarifnr]:read()) * 3600
+ tonumber(self.max_park_minute[self.tarifnr]:read()) * 60
-- print("HH",hoechstparkdauer)
self.parkzeitende_sec = math.ceil(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)
if self.parkzeitende_sec >= akt_time + hoechstparkdauer then
self.hoechstparkdauer_ist_erreicht = 1
self.parkzeitende_sec = 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_hm = "23:59"
self.parkzeitende_day = self:date(t('DATUM'),akt_time)
parkdauer_in_sec = math.ceil(akt_time/86400) * 86400 - akt_time - 40
end
else
self.parkzeitende_hm = self:date(t('ZEIT'),self.parkzeitende_sec)
self.parkzeitende_day = self:date(t('DATUM'),self.parkzeitende_sec)
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(akt_time/86400)
if zusatztage == 1 then
zusatztage = "+ 1 " .. t("Tag")
elseif zusatztage > 1 then
zusatztage = "+ " .. tostring(zusatztage) .. " " .. t("Tage")
else
zusatztage = ""
end
self.anzeige_parkdauer:write(self.parkzeitende_hm)
self.anzeige_zusatztag:write(zusatztage)
self.hour = math.floor((parkdauer_in_sec+30)/3600)
self.min = math.floor(((parkdauer_in_sec+30) - self.hour*3600)/60)
-- 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
nr == 1 and self.amount_in_euro >= tonumber(self.min_sum[self.tarifnr]:read()) then
self.last_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)
end
self.qrcode_text:write("http://12park.de/pay?serial="..
SERIALNUMBER.."&order="..self.qr_ordernr.."&amount="..tostring(self.amount_in_euro*100))
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.reset_coin (self)
self.anzeige_drucken:set_visiable(1)
self.anzeige_betrag:write('0,00')
local fff = self:date(t('%H:%M'))
self.anzeige_parkdauer:write(fff)
self.sondertarif_anzeige:set_visiable(1)
self.qrzaehler = 0
self.tarifnr = 1
self.ticket_wird_gerade_gedruckt = 0
self.real_coins = 0
self.hoechstparkdauer_ist_erreicht = 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)
self.tarif_table = { { {0,0} }, { {0,0} } }
self.broetchen = 0
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 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
end
end
--**********************************************************************
function App.compute_parkdauer (self)
if self.tarif_table[self.tarifnr] == nil then
local pph = fcheck(0,tonumber,self.price_per_hour[self.tarifnr]:read())
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
return parkdauer_in_sec
else
local xa = 0
local ya = 0
local xb = 0
local yb = 99999999
for i,entry in ipairs(self.tarif_table[self.tarifnr]) do
local stunde = tonumber(entry[1])
local preis = tonumber(entry[2])
if self.amount_in_euro <= tonumber(preis) then
if preis < yb or preis == yb and stunde < xb then
xb = stunde
yb = preis
end
end
if self.amount_in_euro >= preis then
if preis > ya or preis == ya and stunde > xa then
xa = stunde
ya = preis
end
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.print_parkticket (self) -- laeuft nur auf dem coin screen
self.coin:activate()
self.anzeige_drucken:set_visiable(0) -- hier die Anzeige 'Ticket wird gedruckt' einblenden
self.ticket_wird_gerade_gedruckt = 1
self.bargeldlos:set_visiable(-1)
start_timer(2,2400,0,1)
local parkticket = string.format("%1u",self.hour)..' '..t('Std.')..' '..string.format("%1u",self.min)..' '..t('Min.')
-- if self.parkzeitende_hm == '23:59' then
-- parkticket = t('ganzer Tag')
-- end
local ust = '---DEL---'
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
ust = t('davon') .. ' ' .. self.mwst .. '% ' .. t('Umsatzsteuer')..': '..trim(self.umsatzsteuer)..' '..CURRENT
if #ust > 32 then
ust = t('davon') .. ' ' .. self.mwst .. '% ' .. t('Umsatzst.')..': '..trim(self.umsatzsteuer)..' '..CURRENT
end
end
local bargeld = t('Barzahlung')
if self.real_coins == 0 then
bargeld = t('bargeldlos')
end
local datum1 = self:date(t('DATUM'))
local parkscheinvom = t('Parkschein vom')..' '..datum1..' '..self:date(t('ZEIT')):gsub('^0','')
local parkpreis = t('Preis')..': '..trim(self.anzeige_betrag:read())..' EUR'..' '..bargeld
local park_dauer = t('Parkdauer')..': '..parkticket
local parken_bis = t('Parken bis')..': '
local loc1 = self.location1:read()
local loc2 = self.location2:read()
local loc3 = self.location3:read()
local sign_text = parkscheinvom .. parkpreis .. ust .. park_dauer .. parken_bis .. self.parkzeitende_day
.. self.parkzeitende_hm:gsub('^0','') .. loc1 .. loc2 .. loc3
sign_text = sign_text:gsub(' ','')
local parkschein_sign = self:crypt(sec_key3, sign_text)
local parkticket = {
'',
'',
'QQx1d@',
'QW',
'QW',
'QQx1ba0QQx1d!QQx00',
parkscheinvom,
parkpreis,
ust,
park_dauer,
parken_bis,
'',
'QQx1d!QQx11QQx1ba1'..self.parkzeitende_day,
'QQx1d!QQx55QQx1ba1'..self.parkzeitende_hm:gsub('^0',''),
'QQx1d!QQx00',
loc1,
loc2,
loc3,
'',
string.sub(parkschein_sign,1,32),
string.sub(parkschein_sign,33,64),
'',
'',
'',
'QQx1dVQQx01',
'QW',
'QW',
'QQx1d@',
}
if self.tarifnr == 2 then
print("PPP-------------------------------------PPP",self.aktuelle_pin_eingabe)
self.used_pins[tostring(self.aktuelle_pin_eingabe)] = math.ceil(tonumber(self:time())/86400)
end
local voucher = self.custom_voucher()
if voucher ~= nil then
for zeile in string.gmatch(voucher,'[^\n]+') do
table.insert(parkticket,zeile)
end
end
local sign_text = parkscheinvom .. parkpreis .. ust .. park_dauer .. parken_bis .. self.parkzeitende_day
.. self.parkzeitende_hm:gsub('^0','') .. loc1 .. loc2 .. loc3
sign_text = sign_text:gsub(' ','')
local parkschein_sign = self:crypt(sec_key3, sign_text)
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 = {}
else
local reports = self:read_report() -- die Daten in das Ticketreporting hineinschreiben
self:update_counter( -math.max(100, self.amount_in_euro*POINTS_PER_EURO ) )
local current = "Q"
if self.real_coins > 0 then
current = string.sub(CURRENT,1,1)
end
self:write_report(reports,self:date("%y%m%d%H%M%S")..current,self.anzeige_betrag:read())
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,5000,0,1)
local sum = tonumber( self.anzahl_gedruckte_parktickets:read() )
self.anzahl_gedruckte_parktickets:write( string.format("%1u",sum+1) )
self:send_to_printer(parkticket)
if file_open == nil then
for i,zeile in ipairs(parkticket) do
print(zeile)
end
end
end
--**********************************************************************
function App.custom_voucher (self)
return nil
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
return fcheck_erg
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
for i,count in ipairs(self.counter) do
if count0 == nil or tonumber(count:read()) < tonumber(count0:read()) then
count0 = count
end
end
sum = tonumber( count0:read() )
count0:write( string.format("%1.0f",sum+betrag) )
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)
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 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' 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)
local reports = {}
local text = 'x'
local signature = ''
local checknr = ''
local text1 = ''
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
if file_open == nil then
local f = io.open('newreport.txt')
if f == nil then
text = ''
else
for zeile in f:lines() do
if trim(zeile) ~= '' then
if signature == '' then
signature = zeile
elseif checknr == '' then
checknr = zeile
else
table.insert(reports,zeile)
text = text .. zeile .. '\n'
end
end
end
f:close()
end
else
local text = my_read_filedata(sd_dir..'/newreport.txt')
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
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
local reportnr = fcheck(0,tonumber,self.reportnumber:read())
if reportnr == nil then
reportnr = 0
end
local akt_count = self:read_counter()
if SERIALNUMBER .. '/' .. string.format('%1u',akt_count) .. '/' .. string.format('%1u',reportnr) ~= trim(checknr) then
self.report_is_integer = 0
end
-- self.report_is_integer = 1 -- fuer Testzwecke @@
return(reports)
end
--**********************************************************************
function App.write_report (self,reports,datum,betrag)
if betrag ~= nil then
table.insert(reports,tostring(datum)..' '..trim(betrag))
end
local reportnr = fcheck(0,tonumber,self.reportnumber:read())
-- print('Q1',reportnr)
if reportnr == nil then
reportnr = 0
end
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 signature = '---'
if self.report_is_integer == 1 then
local md5key = md5lib.hash(text1)
signature = self:crypt(sec_key1, md5key)
end
-- self.debug1:write("WRITEREPORT_SIGNA"..signature)
if file_open == nil then
local f = io.open('newreport.txt','w')
-- print('INIT',f,sd_dir,'--')
if f == nil then
return nil
end
f:write(signature..'\n')
f:write(checknr..'\n')
for i,zeile in ipairs(reports) do
f:write(zeile..'\n')
end
f:close()
else
text = signature .. '\n' .. checknr .. '\n'
for i,zeile in ipairs(reports) do
text = text .. zeile .. '\n'
end
my_write_filedata(sd_dir..'/newreport.txt',text,over_write)
if math.random(1,10) == 1 then
text = my_read_filedata(sd_dir..'/newreport.txt')
if text == nil or text == "" then
self.there_is_no_sdcard = 1
end
end
end
return(1)
end
--**********************************************************************
function App.generate_report (self)
local reports = self:read_report()
if #reports == 0 then
return 0
end
-- 1. die beiden report-spalten berechnen
local summe = 0.00
local total = 0.00
local datum = ''
local datum0 = ''
local betrag = ''
local spalten = { {}, {} }
local nr = 1 -- Spaltennummer, bei nr = 3 ist das Ticket fertig
zeilennr = 1
local max_zeilen = fcheck(0,tonumber,self.max_zeilen:read())
-- print ("MAX_ZEILEN",max_zeilen)
if max_zeilen == nil then
max_zeilen = 30
else
max_zeilen = max_zeilen - 6
end
max_zeilen = math.max(max_zeilen,25)
local heute = self:date(t('DATUM'))
local zeile = ''
print (max_zeilen,#reports)
if self.squeeze_out == nil then
if #reports < max_zeilen - 2 then
return 0
end
end
local mwst = fcheck(0,tonumber,self.tax_vat:read())
if mwst == nil then
mwst = 0
end
self.umsatzsteuersatz = tonumber(mwst)
mwst = (mwst * 1000 + 1)/100
if math.floor(mwst % 10) == 0 then
mwst = string.format('%2u',math.floor(mwst/10))
else
mwst = trim(string.format('%4.1f',mwst/10))
mwst = mwst:gsub('%.',',')
end
self.report_wird_gedruckt:set_visiable(1)
start_timer(26,WAIT_26,0,1)
-- self:renew_timeout()
coroutine.yield()
local xz = 0
while 0 == 0 do -- gehe durch alle newreport-Zeilen
xz = xz + 1
if xz % 3 == 0 then
start_timer(26,WAIT_26,0,1)
coroutine.yield()
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] ],1,3) == '---'
or #spalten[2] > 0 and string.sub(spalten[2][1],1,3) == '---'
or #spalten[2] > 0 and string.sub(spalten[2][1]..' ',1,1) == ' '
or #spalten[1] > 0 and string.sub(spalten[1][#spalten[1]]..' ',1,1) == ' '
-- 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
nr = 2
if trim(spalten[2][1]) == '' then
table.remove(spalten[2],1)
end
end
local proceed = 1
if #reports > 0 then
local xz1 = 1
while xz1 < 100 do -- Format Check fuer Zeilen im newreport.txt
xz1 = xz1 + 1
zeile = table.remove(reports,1)
datum = string.sub(zeile,1,14)
current = string.sub(zeile,15,15)
betrag = trim( string.sub(zeile,16):gsub(',','.') )
local x1 = fcheck(-99,tonumber,datum)
local x2 = fcheck(-99,tonumber,betrag)
if x1 ~= -99 and x2 ~= -99 then
break
end
end
-- print('ZZ',zeile)
uhrzeit = self:date(t('ZEIT'),tonumber(datum))
datum = self:date(t('DATUM'),tonumber(datum))
-- print(self:date(t("%y%m%d%H%M%S")))
if datum0 ~= datum and #spalten[2] > max_zeilen - 12 then proceed = 0
elseif #spalten[2] > max_zeilen - 8 then proceed = 0
-- elseif datum0 ~= '' and datum0 ~= datum and datum == heute then proceed = 0
end
if proceed == 0 then
table.insert(reports,1,zeile)
datum0 = 'xxx'
end
else
datum0 = 'xxx'
end
if datum0 ~= '' and datum0 ~= datum then
if summe < 9.99 then
table.insert(spalten[nr],' ------')
elseif summe < 99.99 then
table.insert(spalten[nr],' -------')
else
table.insert(spalten[nr],' --------')
end
table.insert(spalten[nr],' '..string.format('%7.2f',summe):gsub('%.',',') .. ' ' .. string.sub(CURRENT,1,1))
table.insert(spalten[nr],' ')
summe = 0.00
end
if datum0 ~= 'xxx' and datum0 ~= datum then
table.insert(spalten[nr],datum)
table.insert(spalten[nr],'----------')
datum0 = datum
end
if datum0 ~= 'xxx' then
-- print(betrag)
betrag = tonumber(betrag)
-- print(betrag)
summe = summe + betrag
total = total + betrag
betrag = string.format('%7.2f',betrag)
betrag = betrag:gsub('%.',',')
table.insert(spalten[nr],uhrzeit..betrag..' '..current)
end
if datum0 == 'xxx' then
-- print('BREAK',#spalten[1],#spalten[2])
break
end
datum0 = datum
end
-- while #spalten[2] < max_zeilen - 4 do
-- table.insert(spalten[2],'')
-- end
local ust = self.umsatzsteuersatz / ( 100 + self.umsatzsteuersatz ) * total
local netto = 100 / ( 100 + self.umsatzsteuersatz ) * total
if nr == 1 and #spalten[1] > max_zeilen - 9 or #spalten[2] < 2 then
nr = 2
end
if #spalten[nr] > 0 then
table.insert(spalten[nr],'')
end
table.insert(spalten[nr],'NETTO:' .. string.format('%8.2f',netto):gsub('%.',','))
if #mwst == 2 then
table.insert(spalten[nr],t('USt')..mwst..'%' .. string.format('%8.2f',ust):gsub('%.',','))
else
table.insert(spalten[nr],t('USt')..mwst..'%' .. string.format('%6.2f',ust):gsub('%.',','))
end
table.insert(spalten[nr],'TOTAL:' .. string.format('%8.2f',total):gsub('%.',','))
-- 2. Ausgabefile definieren
local reportnr = fcheck(0,tonumber,self.reportnumber:read())
if reportnr == nil then
reportnr = 0
end
-- local akt_count = self:read_counter()
reportnr = reportnr + 1
if #reports == 0 then
-- do_next_report = 0
if self.squeeze_out == nil then -- um zu verhindern, dass 'halbe' reports ausgedruckt werden
return
end
end
local 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')
-- 3. die Spalten sind fertig, jetzt das eigentliche Protokoll schreiben
local text1 = {}
table.insert(text1,t('Protokoll verkaufte Parkscheine'))
table.insert(text1,t(self.location1:read()))
table.insert(text1,t('Protokoll-Nr.')..' '..SERIALNUMBER..' / '..string.format('%03u',reportnr))
table.insert(text1,string.sub(t('Signatur').." ",1,10))
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]
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
start_timer(26,WAIT_26,0,1)
-- self:renew_timeout()
coroutine.yield()
-- 4. Signatur berechnen
local signa_report = ''
if self.report_is_integer == 1 then
local text = ''
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(' ','')
local md5_report = ( md5lib.hash(text) )
signa_report = base27.encode( bin.hextos( self:crypt(sec_key1, md5_report) ) )
signa_report = table.remove(text1) .. signa_report
end
-- 5. jetzt den Text des Reports schreiben
-- local text = {}
-- for i,zeile in ipairs(text1) do
-- table.insert(text,zeile)
-- end
-- nr = math.ceil(#signa_report/2)
if signa_report == '' then
table.remove(text1)
table.insert(text1,' ')
table.insert(text1,t('Nicht signierter Ausgabereport.'))
table.insert(text1,t('Nur nachrichtliche Werte.'))
table.insert(text1,t('Kann nicht als Umsatznachweis'))
table.insert(text1,t('fuer die Steuerbehoerden'))
table.insert(text1,t('verwendet werden.'))
else
table.insert(text1,string.sub(signa_report,1,32))
table.insert(text1,string.sub(signa_report,33,64))
end
table.insert(text1,' ')
for i,zeile in ipairs(text2) do
table.insert(text1,zeile)
end
start_timer(26,WAIT_26,0,1)
coroutine.yield()
-- 6. jetzt die verbliebenen Ticket-eintraege zurueck in die reports-Datei schreiben
if total > 0.00 then
self.reportnumber:write(string.format('%8u',reportnr))
self:write_report(reports)
end
-- 7. jetzt den neuen Report in die Datei schreiben und gleichzeitig drucken
if file_open == nil and total > 0.00 then
for i,zeile in ipairs(text1) do
print(zeile)
end
fc = io.open(reportfile,'w')
if fc ~= nil then
for i,zeile in ipairs(text1) do
fc:write(zeile..'\n')
end
fc:close()
end
elseif total > 0.00 then
local rtxt = {
' ',
'QQx1d@',
'QW',
'QW',
'QQx1ba0QQx1d!QQx00',
' '
}
self.debug1:write("123")
local filetxt = ''
for i,zeile in ipairs(text1) do
table.insert(rtxt,string.sub(zeile,1,32))
filetxt = filetxt..zeile..'\n'
end
if add_leerzeile then
table.insert(rtxt,' ')
end
table.insert(rtxt,t('www.12park.de'))
filetxt = filetxt..t('www.12park.de')..'\n'
table.insert(rtxt,' ')
table.insert(rtxt,' ')
table.insert(rtxt,' ')
table.insert(rtxt,' ')
table.insert(rtxt,'QQx1dVQQx01')
table.insert(rtxt,'QW')
table.insert(rtxt,'QW')
table.insert(rtxt,'QQx1d@')
-- table.insert(rtxt,'========')
self.debug1:write("124")
local rtxt1 = ""
for i,zeile in ipairs(rtxt) do
rtxt1 = rtxt1 .. zeile .. "\r\n"
end
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
my_write_filedata(sd_dir..'/'..reportfile,filetxt,over_write)
self.debug1:write("125")
if self.squeeze_out == nil then
local printtext = my_read_filedata(sd_dir..'/printbuffer.txt')
if printtext == nil then
printtext = ""
end
rtxt1 = printtext .. rtxt1
my_write_filedata(sd_dir..'/printbuffer.txt',rtxt1,over_write)
else
self.debug1:write("126")
self:send_to_printer(rtxt)
self.debug1:write("127")
end
end
if self.squeeze_out == nil then
start_timer(25,3*WAIT_26,0,1)
end
end
--**********************************************************************
function App.no_simulation (self)
local kn1 = self
local simulation = "no"
end
--**********************************************************************
function App.print_report (self)
if file_open ~= nil then
local text = my_read_filedata(sd_dir..'/printbuffer.txt')
local prdata = {}
for zeile in string.gmatch(text,'[^\n]+') do
table.insert(prdata,zeile)
end
if #prdata < 2 and self.press_protocol > 1 then
WAIT_26 = 10 -- Intervall, in dem auf den naechsten Block der Signatur-Berechnung
self.squeeze_out = 1 -- gewartet wird. Alle Ticketreports werden gedruckt.
self.press_protocol = 1
start_timer(25,10,0,1)
else
-- if #prdata > 2 then
self.report_wird_gedruckt:set_visiable(1)
-- end
self:send_to_printer(prdata)
my_write_filedata(sd_dir..'/printbuffer.txt',' ',over_write)
end
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)) )
local rs = fcheck(0,tonumber,self.pin_start:read())
if rs == nil then
rs = 0
end
if 1000000 <= 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
end
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
local reports = self:read_report()
self.report_is_integer = 1
self:write_report(reports)
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
local reports = self:read_report()
self.ticketslot_pin:write(pin1 .. stored_pins)
self:update_counter(300000)
self.report_is_integer = ri
self:write_report(reports)
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)
self.not_ready:activate()
end
self.no_timeout = 1
-- self.uart_sperre = 1
self.last_was_press = 0
DISABLED = 1
end
--**********************************************************************
function App.on_timer (self,tid)
-- print('Timer call'..tostring(tid))
if tid == 0 then
-- self.uart_sperre = 0
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
self:write_report(reports,self:date("%y%m%d%H%M%S")..CURRENT,betr)
self.anzeige_betrag:write('0,00')
end
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
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.long_press = 1
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 == 18 then
self.no_timeout = 0
if self.qrzaehler > 5 then
self:on_timer(2)
else
self.coin:activate()
end
self:renew_timeout()
elseif tid == 19 then
self.qr_invalid:set_visiable(-1)
self.qrtan:write("")
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
self:send_to_printer1( self.print_data )
start_timer(24,1,0,1)
elseif tid == 24 then
self.report_wird_gedruckt:set_visiable(0)
elseif tid == 25 then
-- self.uart_sperre = 0
self.debug1:write("Start to generate report with thread")
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 == 29 then
start_timer(29,60000,0,1)
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 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
end
self:on_control_notify1( self:current_screen() ,coin_nr)
elseif packet[2] == 21 then
-- print("PP",21,pr_re)
-- table.insert(self.uartbuffer,1)
-- if self.uart_sperre == 1 then
-- return
-- end
self.last_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
-- if self.uart_sperre == 1 then
-- return
-- end
-- print("PP",22)
-- table.insert(self.uartbuffer,0)
if pr_re > 0 then
stop_timer(9)
end
-- set_text(1,22,"RELEASE")
-- set_text(1,21,"--")
-- print("LWP",self.last_was_press)
if self.last_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_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_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
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_was_press)
if self.last_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_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_was_press = 1
if pr_re < 2 then
self:on_control_notify1( self:current_screen().screen_sid , 80)
end
else
if self.last_was_press == 1 then
self.last_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_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_was_press == 1 and self.long_press == 1 then
self:on_control_notify1( self:current_screen().screen_sid , 80)
end
self.last_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
--**********************************************************************
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 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)
end
end
self.act_lang = lang_nr
self.app.press_release = self.pr_re
end
--**********************************************************************
Field = {}
Field.__index = Field
function Field:new(app,default_text,screen,control_nr)
local self = setmetatable({},Field)
self.app = app
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]
screen:set_text(control_nr,flash_store)
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
--**********************************************************************
-- CONSOLE AREA
--**********************************************************************
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
local fh = io.open('patch_'..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 md5key = md5lib.hash(text1)
local signature = App.crypt('',sec_key1, md5key)
local f = io.open('newreport.txt','w')
f:write(signature..'\n')
f:write(text1..'\n')
f:close()
end
--*************************************************************************
if arg ~= nil and arg[1] == 'tan' then
local x = App:new(1)
print('-----------------------------------------------')
x:compute_tan(arg[2])
print('-----------------------------------------------')
end
--*************************************************************************
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 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
--*************************************************************************
--if arg ~= nil and arg[1] == "decr" then
-- local jj = load_patch("patch_0522789.enc")
--end
--*************************************************************************
if arg ~= nil and arg[1] == "ttime" then
zaehler = 1
st = 1611866900
while zaehler < 200 do
st = st + math.random(1000,20000)
cu = math.random(1,2)
cu = string.sub("EQ",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
--**********************************************************************
-- TRANSLATIONS
--**********************************************************************
translation_table = [[
;LCODE;DE;EN;FR;CZ;DK;ES;FI;GR;HU;IE;IT;NL;NO;PL;RO;RU;SE;TR;
;LANG;Deutsch;English;Français;Čeština;Dansk;Español;Suomalainen;Ελληνικά;Magyar;Gaeilge;Italiano;Nederlands;Norsk;Polskie;Română;Русский;Svenska;Türk;
;S2F270;Bitte Münzen einwerfen;Please insert coins;Veuillez insérer des;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;
;S2F271;-;-;pièces;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;
;S2F171;Bargeldlos bezahlen:;Cashless payment:;Payer sans argent liquide:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;
;S3F171;Bargeldlos bezahlen:;Cashless payment:;Payer sans argent liquide:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;
;S15F171;Bargeldlos bezahlen:;Cashless payment:;Payer sans argent liquide:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;
;S3F271;Parkzeit;Parking time;Temps de stationnement;Parking time;Parking time;Parking time;Parking time;Parking time;Parking time;Parking time;Parking time;Parking time;Parking time;Parking time;Parking time;Parking time;Parking time;Parking time;
;S3F270;Für Ticket Knopf drücken;Press button for ticket;Appuyez sur le bouton;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;
;S3F277;-;-;pour le billet;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;
;S3F278;Bitte Münzen einwerfen;Please insert coins;Veuillez insérer des;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;
;S3F279;-;-;pièces;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;
;BITTEMUENZENA;Bitte Münzen einwerfen;Please insert coins;Veuillez insérer des;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;
;BITTEMUENZENB;-;-;pièces;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;
;KNOPFDRUECKENA;Für Ticket Knopf drücken;Press button for ticket;Appuyez sur le bouton;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;
;KNOPFDRUECKENB;-;-;pour le billet;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;
;S3F267;Ticket wird gedruckt;Ticket is printed;Le billet est imprimé;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;
;S3F201;Sondertarif;Special rate;Tarif spécial;Special rate;Special rate;Special rate;Special rate;Special rate;Special rate;Special rate;Special rate;Special rate;Special rate;Special rate;Special rate;Special rate;Special rate;Special rate;
;S5F217;Hauptmenü;Main menu;Menu principal;Main menu;Main menu;Main menu;Main menu;Main menu;Main menu;Main menu;Main menu;Main menu;Main menu;Main menu;Main menu;Main menu;Main menu;Main menu;
;S5F202;Lokale Einstellungen;Local settings;Paramètres locaux;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;
;S5F201;Verfügbare Punkte:;Available points:;Points disponibles:;Available points:;Available points:;Available points:;Available points:;Available points:;Available points:;Available points:;Available points:;Available points:;Available points:;Available points:;Available points:;Available points:;Available points:;Available points:;
;S5F212;Tarif;Rate;Tarif;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;
;S5F213;Protokoll;Protocol;Protocole;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;
;S5F205;Erweitert;Extended;Additionnel;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;
;S5F299;Beenden;Finish;Rompre;Finish;Finish;Finish;Finish;Finish;Finish;Finish;Finish;Finish;Finish;Finish;Finish;Finish;Finish;Finish;
;S5F204;Tickets seit Neustart:;Tickets since restart:;Billets depuis redémarrage:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;
;S6F203;Mindestbetrag;Minimum amount;Montant minimal;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;
;S6F208;Preis pro Stunde;Price per hour;Prix par heure;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;
;S6F201;Mehrwertsteuer %;VAT %;T.V.A. %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;
;S6F204;Tarif;Rate;Tarif;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;
;S6F207;Höchstparkdauer;Maximum parking;Maximale de;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;
;S6F206;;;stationnement;;;;;;;;;;;;;;;;
;S6F211;Tagesticket;Day ticket;Billet journalier;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;
;S6F215;Stunden;Hours;Heures;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;
;S6F216;Minuten;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;
;S6F217;Nein;No;Non;No;No;No;No;No;No;No;No;No;No;No;No;No;No;No;
;S6F218;Ja;Yes;Oui;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;
;S6F220;Münzen;Coins;Monnaie;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;
;S6F221;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;
;S6F222;Mü+QR;Co+QR;Mo+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;
;S6F233;Zahlungsweise;Payment mode;Mode de paier;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;
;S6F233;Tabelle;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;
;S6F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S6F219;Weiter zu Tarif 2;Go to tariff 2;Aller au tarif 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;
;S7F204;Erweitert;Extended;Additionnel;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;
;S7F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S7F201;TAN Freigabe für Tickets;Charging TAN for tickets;TAN de recharge des billets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;
;S7F212;PIN Verwaltung;PIN Administration;Administration PIN;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;
;S7F203;Neue PIN Liste;New PIN list;Nouvelle liste des codes PIN;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;
;S7F208;Gerät;Device;Appareil;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;
;S8F202;Sprache;Language;Langue;Language;Language;Language;Language;Language;Language;Language;Language;Language;Language;Language;Language;Language;Language;Language;
;S8F203;Standort 1;Location 1;Emplacement 1;Location 1;Location 1;Location 1;Location 1;Location 1;Location 1;Location 1;Location 1;Location 1;Location 1;Location 1;Location 1;Location 1;Location 1;Location 1;
;S8F204;Lokale Einstellungen;Local settings;Paramètres locaux;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;
;S8F207;Datum;Date;Date;Date;Date;Date;Date;Date;Date;Date;Date;Date;Date;Date;Date;Date;Date;Date;
;S8F206;Zeit;Time;Temps;Time;Time;Time;Time;Time;Time;Time;Time;Time;Time;Time;Time;Time;Time;Time;
;S8F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S8F205;Standort 2;Location 2 ;Emplacement 2;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;
;S8F211;Standort 3;Location 3;Emplacement 3;Location 3;Location 3;Location 3;Location 3;Location 3;Location 3;Location 3;Location 3;Location 3;Location 3;Location 3;Location 3;Location 3;Location 3;Location 3;
;S9F213;Drucken...;Print...;Imprimer...;Print...;Print...;Print...;Print...;Print...;Print...;Print...;Print...;Print...;Print...;Print...;Print...;Print...;Print...;Print...;
;S9F204;Protokoll;Protocol;Protocole;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;
;S9F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S9F203;Zeilen pro Seite;Lines per page;Lignes par page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;
;S9F202;Protokoll Zähler:;Protocol counter:;Compteur de journaux:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;
;S9F205;Report wird erstellt...;Ticket is created...;Le report est creé...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;
;S10F270;Gerät nicht betriebsbereit;Device not ready;Appareil non en service;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;
;S11F280;Sprach Auswahl;Language selection;Sélection de la langue;Language selection;Language selection;Language selection;Language selection;Language selection;Language selection;Language selection;Language selection;Language selection;Language selection;Language selection;Language selection;Language selection;Language selection;Language selection;
;S11F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S12F280;PIN Eingabe;PIN entry;Entrée PIN;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;
;S12F202;Ungültige PIN;Invalid PIN;Code PIN invalide;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;
;S14F280;TAN Freigabe;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;
;S14F217;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S14F207;TAN anfordern;Order a TAN;Commander un TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;
;S15F270;Bitte Münzen einwerfen;Please insert coins;Veuillez insérer des;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;
;S15F271;;;pièces;;;;;;;;;;;;;;;;
;S16F270;Bitte Münzen einwerfen;Please insert coins;Veuillez insérer des;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;
;S16F271;;;pièces;;;;;;;;;;;;;;;;
;S18F262;PIN Verwaltung;PIN Administration;Administration PIN;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;
;S18F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S21F204;Neue PIN Liste;New PIN list;Liste des codes PIN;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;
;S21F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S21F270;Neue PIN Liste erzeugen.;Create new PIN list.;Créer une nouvelle liste de codes PIN;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;
;S21F217;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?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;
;S21F205;Abbrechen;Abort;Avorter;Abort;Abort;Abort;Abort;Abort;Abort;Abort;Abort;Abort;Abort;Abort;Abort;Abort;Abort;Abort;
;S22F203;Mindestbetrag;Minimum amount;Montant minimal;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;
;S22F208;Preis pro Stunde;Price per hour;Prix par heure;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;
;S22F201;Mehrwertsteuer %;VAT %;T.V.A. %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;
;S22F204;Tarif 2;Rate 2;Tarif 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;
;S22F207;Höchstparkdauer;Maximum parking;Maximale de;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;
;S22F206;;;stationnement;;;;;;;;;;;;;;;;
;S22F220;Münzen;Coins;Monnaie;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;
;S22F221;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;
;S22F222;Mü+QR;Co+QR;Mo+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;
;S22F211;Tagesticket;Day ticket;Billet journalier;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;
;S22F215;Stunden;Hours;Heures;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;
;S22F216;Minuten;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;
;S22F217;Nein;No;Non;No;No;No;No;No;No;No;No;No;No;No;No;No;No;No;
;S22F218;Ja;Yes;Oui;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;
;S22F233;Tabelle;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;
;S22F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S23F220;Werkseinstellung bei Neustart;Factory setting on restart;Réglage d'usine au redémarrage;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;
;S23F203;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;
;S23F202;Version;Version;Version;Version;Version;Version;Version;Version;Version;Version;Version;Version;Version;Version;Version;Version;Version;Version;
;S23F204;Gerät;Device;Appareil;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;
;S23F205;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;
;S23F206;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;
;S23F207;Speicher bereinigen;Clean up memory;Nettoyer la mémoire;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;
;S23F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S23F201;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;
;S23F217;Nein;No;Non;No;No;No;No;No;No;No;No;No;No;No;No;No;No;No;
;S23F218;Ja;Yes;Oui;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;
;S26F270;Bitte SD Karte einlegen;Please insert SD card;Veuillez insérer la carte SD;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;
;PARKSCHEIN;Parkschein;Parkticket;Ticket de parking;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;
;DATUM;%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;%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;
;UST;Umsatzsteuer;VAT;TVA;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;
;USTK;Umsatzst.;VAT;TVA;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;
;MINDESTENS;mind.;at least;au moins;at least;at least;at least;at least;at least;at least;at least;at least;at least;at least;at least;at least;at least;at least;at least;
;TAGESTICKET;Tagesticket ab;Dayticket from;Billiet jour de;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;
;TAG;Tag;day;jour;day;day;day;day;day;day;day;day;day;day;day;day;day;day;day;
;TAGE;Tage;days;jours;days;days;days;days;days;days;days;days;days;days;days;days;days;days;days;
;S27F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S27F5;QR Code scannen, TAN bekommen, eingeben;Scan QR code, get TAN, enter;Scan QR code, obtenir TAN, taper;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;
;S27F3;Ungültig;Invalid;Invalide;Invalid;Invalid;Invalid;Invalid;Invalid;Invalid;Invalid;Invalid;Invalid;Invalid;Invalid;Invalid;Invalid;Invalid;Invalid;
;S28F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S29F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S28F215;Stunden;Hours;Heures;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;
;S28F216;Preis;Price;Prix;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;
;S28F217;Stunden;Hours;Heures;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;
;S28F218;Preis;Price;Prix;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;
;S29F215;Stunden;Hours;Heures;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;
;S29F216;Preis;Price;Prix;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;
;S29F217;Stunden;Hours;Heures;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;
;S29F218;Preis;Price;Prix;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;
;PROSTD;pro Stunde;per hour;par heure;per hour;per hour;per hour;per hour;per hour;per hour;per hour;per hour;per hour;per hour;per hour;per hour;per hour;per hour;per hour;
;DAVON;davon;from that;de la;from that;from that;from that;from that;from that;from that;from that;from that;from that;from that;from that;from that;from that;from that;from that;
;PARKSCHEINVOM;Parkschein vom;Parkticket from;Ticket de parking de;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;
;GUTSCHEINVOM;Gutschein vom;Voucher from;Bon d achat de;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;
;PARKDAUER;Parkdauer;Parking duration;Duree de parking;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;
;PARKENBIS;Parken bis;Parking until;Parking d ici a;Parking until;Parking until;Parking until;Parking until;Parking until;Parking until;Parking until;Parking until;Parking until;Parking until;Parking until;Parking until;Parking until;Parking until;Parking until;
;GUELTIGBIS;Gueltig bis;Valid until;Valable jusqu a;Valid until;Valid until;Valid until;Valid until;Valid until;Valid until;Valid until;Valid until;Valid until;Valid until;Valid until;Valid until;Valid until;Valid until;Valid until;
;STD;Std.;h;h;h;h;h;h;h;h;h;h;h;h;h;h;h;h;h;
;MIN;Min.;min;min;min;min;min;min;min;min;min;min;min;min;min;min;min;min;min;
;BARZAHLUNG;Barzahlung;Cash payment;Payment comptant;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;
;BARGELDLOS;bargeldlos;cashless;sans argent liqu;cashless;cashless;cashless;cashless;cashless;cashless;cashless;cashless;cashless;cashless;cashless;cashless;cashless;cashless;cashless;
]]
--************************************************************************