-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathClasses.lua
More file actions
1301 lines (1194 loc) · 52.4 KB
/
Classes.lua
File metadata and controls
1301 lines (1194 loc) · 52.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
-----LUA-----
local function Classes()
-- Utility functions shared across all classes, can also be used stand-alone
local function Helper()
local self = {}
local _card_types = { "io", "lte_serial", "ethernet", "adio", "lte", "serial", "sio", "dsl", "sfp", "power" }
local _cards = {}
local _device_type = cli("status.device_info.device_type")
-- Detect all inserted MR Cards and store them in _cards
-- need to change if same board_types are hereg
local function _cards_table()
for _, b in pairs(_card_types) do
for n = 1, 5 do
local exist = pcall(cli, "status." .. b .. n)
if exist and not _cards[cli("status.device_info.slot[" .. n .. "].board_type") .. n] then
_cards[cli("status.device_info.slot[" .. n .. "].board_type") .. n] = b .. n
elseif exist and _cards[cli("status.device_info.slot[" .. n .. "].board_type") .. n] then
_cards[cli("status.device_info.slot[" .. n .. "].board_type") .. n.."a"] = b..n
end
end
end
return _cards
end
-- Scan cli list output and collect indices whose <entry> matches <pattern> (eg. entry: description, pattern: 'TCP')
-- entry parameter: specifies by what the index is searched for (eg. name, description)
-- pattern parameter: pattern inside the given the given entry (eg. inside description search for '123')
local function get_indexes(cli_command, pattern, entry)
local index_table = {}
local _, bracket_count = cli_command:gsub("%[%d+%]", "")
local cl = cli(cli_command)
local lines = self.cli_commands_into_table(cl)
entry = entry and entry:gsub("(%W)", "%%%1") or ""
pattern = pattern and pattern:gsub("(%W)", "%%%1") or ""
for _, v in ipairs(lines) do
if bracket_count == 0 and v:find("%.[%w_]*" .. entry .. "[%w_]*=%s*[^%c]*" .. pattern) then
local index = v:match(".-%[(%d+)%].-=")
local matched_result = v:match(entry .. "=(.+)") -- test with string.format or use v:find(..., 1, true)
index_table[index] = matched_result
elseif bracket_count == 1 and v:find("%[%d+%]%.[%w_]*" .. entry .. "[%w_]*=%s*[^%c]*" ..pattern) then
local index = v:match("%[%d+%]%.[%w_.]*%[(%d+)%].-=")
local matched_result = v:match(entry .. "=(.+)")
index_table[index] = matched_result
end
end
if not next(index_table) then
lua_log("ERROR: Indexes weren't found, because no entry contains " .. pattern)
return false, nil
end
return true, index_table
end
_cards_table()
-- convert a cli output into a table of lines
function self.cli_commands_into_table(str)
local t = {}
for l in (str .. "\n"):gmatch("(.-)\n") do
table.insert(t, l)
end
return t
end
-- Like above, but keeps only lines that match given patterns
-- change content inside of 'l:match()' if you want another pattern to match/extract
-- e.g %f[%a]input%f[%A] means character before and after input has to be a non letter
-- for more specific matches just add an 'or l:match("pattern")'
function self.cli_specifics_into_table(str)
local t = {}
for l in (str .. "\n"):gmatch("(.-)\n") do
if l:match("%f[%a]input%f[%A]") or l:match("%f[%a]output%f[%A]") then
table.insert(t, l)
end
end
return t
end
-- returns device type(eg. MRX3 or MIRO-L200)
function self.get_device_type()
return _device_type
end
-- returns table of MR-Cards
function self.get_card_types()
return _card_types
end
-- returns table of available cards on a router
function self.get_cards()
return _cards
end
-- returns card based on slot (eg. card == ethernet1)
function self.get_card_of_slot(slot)
slot = tostring(slot)
local ok, valid = pcall(cli,"status.device_info.slot[" .. slot .."]")
if not ok then
lua_log("ERROR: Failed to get card in slot " .. slot .. ": " .. valid )
return false, nil
end
local card = _cards[cli(valid .. ".board_type")]
return true, card
end
-- returns board_type of a chosen slot (eg. M3CPU, RMINI)
function self.get_board_type_of_slot(slot)
slot = tostring(slot)
local ok, valid = pcall(cli, "status.device_info.slot[" .. slot .. "]")
if not ok then
lua_log("ERROR: Failed to get board in slot " .. slot .. ": " .. valid)
return false, nil
end
local board = cli(valid .. ".board_type")
return true, board
end
-- adds a new field to every (or a specific) table entry
-- table parameter: table or array
-- entry parameter: name for entry/attribute to be added (eg. for Ports tables, add entry 'mode')
-- value parameter: value for added entry, if not given use default value '---'
-- key parameter: if key is given, add entry to specific key, else add entry to every table element
function self.add_entry(tbl, entry, value, key)
value = value or '---'
if key then
local sub_tbl = tbl[key]
if sub_tbl and not sub_tbl[entry] then
sub_tbl[entry] = value
end
return tbl
end
for _, v in pairs(tbl) do
if not v[entry] then
v[entry] = value
end
end
return tbl
end
-- deletes a field from every (or a specific) table entry
-- Same parameters, but without 'value'
function self.delete_entry(tbl, entry, key)
if key then
local sub_tbl = tbl[key]
if not sub_tbl then
lua_log("ERROR: Field: " .. entry .. " from " .. tbl .. "[" .. key .. "] does not exist")
return false
end
sub_tbl[entry] = nil
return tbl
end
for _, v in pairs(tbl) do
v[entry] = nil
end
return tbl
end
-- lists all entries of a table or subtable (eg. list_table_contents(Ports) or list_table_contents(Ports[key]))
-- used for debugging purposes -> CLI or GUI necessary
function self.list_table_contents(tbl)
if type(tbl) == 'table' then
for k,v in pairs(tbl) do
print(k,v)
end
return true
end
return false
end
-- sorts table by keys
-- compare parameter: specify how a table is sorted (eg. function(a,b) return a < b end for ascending order)
function self.sort_table(tbl, compare)
local keys = {}
for k in pairs(tbl) do table.insert(keys, k) end
table.sort(keys, compare)
local i = 0
return function()
i = i + 1
local k = keys[i]
if k then
return k, tbl[k]
end
end
end
-- get index of first found entry in an endless list
-- entry specifies where to look for eg. description, name; entry can be left empty
function self.get_index(cli, pattern, entry)
local ok, indexes = get_indexes(cli, pattern, entry)
if ok then
for k in self.sort_table(indexes, function(a, b) return a<b end) do
return true, k
end
end
return false, nil
end
-- same as function above but returns a list of indices
function self.get_indexes(cli, pattern, entry)
ok, indexes = get_indexes(cli, pattern, entry)
if ok then
return true, indexes
end
return false, table
end
-- returns first index found from cli list output by name (eg. name="net1")
function self.get_index_by_name(cli, name)
local ok, index_table = get_indexes(cli, name, "name")
if not ok then
return false, nil
end
for k in self.sort_table(index_table, function(a,b) return a < b end) do
return true, k
end
end
-- returns first index found by pattern in description (eg. description='TCP Port 123', pattern == '123')
function self.get_index_by_description(cli, pattern)
local ok, index_table = get_indexes(cli, pattern, "description")
if not ok then
return false, nil
end
for k in self.sort_table(index_table, function(a,b) return a<b end) do
return true, k
end
end
-- returns table of indexes, from cli list output, found by pattern in description
function self.get_indexes_by_description(cli, pattern)
local ok, index_table = get_indexes(cli, pattern, "description")
if not ok then
return false, nil
end
return true, index_table
end
-- sets current wan chain
function self.set_wan_chain(wan)
-- checks if wan chain exists
if wan == cli("status.sysstat.wan.name") then
lua_log("ERROR: Already set to " .. wan)
return false, nil
end
local switch_wan
local ok, switch_wan_index = self.get_index_by_name("wan.wans.wan_chain", wan)
if not ok then
lua_log("ERROR: WAN chain " .. wan .. " does not exist and could not be set")
return false, nil
end
switch_wan = cli("wan.wans.wan_chain[" .. switch_wan_index .. "].name")
-- set and activate wan chain
cli("help.debug.wan_chain.wan_chain=" .. switch_wan)
cli("help.debug.wan_chain.submit")
return true, switch_wan
end
return self
end
-- handle router network interfaces (IP-net, WAN-cards, VPN)
local function Interfaces()
local self = {}
local _vpn_types = {"openvpn", "ipsec", "gre", "dmvpn", "pptp", "pppoe"}
local _net_interfaces = {}
local _wan_interfaces = {}
local _vpn_interfaces = {}
local help = selfs.Helper
local _cards = help.get_cards()
-- return table of available IPnet interfaces
local function _ip_nets()
for ni = 1, cli("interfaces.ip_nets.net.size") do
local net = cli("interfaces.ip_nets.net[" .. ni .. "].name")
_net_interfaces[net] = {net = net,
index = ni,
ip_address = {}}
for ipi = 1, cli("interfaces.ip_nets.net[".. ni .. "].ip_address.size") do
_net_interfaces[net].ip_address[ipi] = {address = cli("interfaces.ip_nets.net[".. ni .. "].ip_address[" .. ipi .. "].ip_address"),
index = ipi}
end
end
return _net_interfaces
end
-- returns table of available WAN-cards that double as interfaces (LTE, DSL)
local function _wans()
for _, v in pairs(_cards) do
if v:match("^lte%d$") then
_wan_interfaces[v] = {net = v}
elseif v:match("^lte_serial%d$") then
local lte, slot = v:match("^(lte)_serial(%d)$")
_wan_interfaces[lte..slot] = {net = lte..slot}
elseif v:find("dsl") then
_wan_interfaces[v] = {net = v}
end
end
return _wan_interfaces
end
-- returns table of VPN tunnels per type (OpenVPN, IPsec)
local function _vpns()
for _, v in pairs(_vpn_types) do
local size = cli("interfaces." .. v .. ".tunnel.size")
if size ~= 0 then
for vi = 1, size do
_vpn_interfaces[v .. vi] = {net = v .. vi,
index = vi}
end
end
end
return _vpn_interfaces
end
-- sets apn of lte modem from a table of apns based on imsi of inserted sim card
local function set_apn(modem, apn_table, run_time, mode)
if not modem:find("lte") then
lua_log("ERROR: Modem '" .. modem .. "' is not a LTE Modem")
end
if not _wan_interfaces[modem] then
lua_log("ERROR: Modem '" .. modem .. "' does not exist")
return false
end
local lte, slot = modem:match("^(lte)_serial(%d)$")
local board = help.get_board_type_of_slot(slot)
local device = help.get_device_type()
if board == "M3PSQ" or device == "ECR-LW300" then
local exists, used_sim = pcall(cli, "status." .. modem .. ".used_sim")
if not exists then
return false
end
if not used_sim:find("1") then
return false
end
end
local exists, imsi_usim, apn
if not run_time then
run_time = true
end
while run_time do
if mode == 'imsi' then
exists, imsi_usim = pcall(cli, "status." .. modem .. ".IMSI")
else
exists, imsi_usim = pcall(cli, "status." .. modem .. ".USIM")
end
if not exists then
return false
end
if imsi_usim:find("^(%d+)$") or run_time == 0 then
break
end
if not type(run_time) == "boolean" then
run_time = run_time - 1
end
sleep(1)
end
for imsis_usims_provider, apn_provider in pairs(apn_table) do
if imsi_usim:find(imsis_usims_provider) then
apn = apn_provider
end
end
if not apn then
lua_log("ERROR: APN could not be found")
return false
end
lua_log("Setting APN to " .. apn)
cli("interfaces." .. lte .. slot .. ".apn=" .. apn)
return true
end
-- returns table of IPnet interfaces
function self.get_ip_net_interfaces()
if next(_net_interfaces) then
for k in pairs(_net_interfaces) do
_net_interfaces[k] = nil
end
end
_ip_nets()
return _net_interfaces
end
-- returns table of VPN interfaces
function self.get_vpn_interfaces()
if next(_vpn_interfaces) then
for k in pairs(_vpn_interfaces) do
_vpn_interfaces[k] = nil
end
end
_vpns()
return _vpn_interfaces
end
-- returns table of interfaces that are also MR-cards
function self.get_wan_interfaces()
if next(_wan_interfaces) then
for k in pairs(_wan_interfaces) do
_wan_interfaces[k] = nil
end
end
_wans()
return _wan_interfaces
end
-- returns table of ip addresses for given ipnet
function self.get_ip_addresses(net)
_ip_nets()
local ip_net = _net_interfaces[net]
if not ip_net then
return false
end
local ip_addresses = {}
for ip_index, ip_contents in ipairs(ip_net.ip_address) do
ip_addresses[ip_index] = {address = ip_contents.address,
index = ip_contents.index}
end
return ip_addresses
end
function self.set_apn_by_imsi(modem, apn_table, run_time)
local success = set_apn(modem, apn_table, run_time, "imsi")
if not success then
return false
end
return true
end
function self.set_apn_by_usim(modem, apn_table, run_time)
local success = set_apn(modem, apn_table, run_time, "usim")
if not success then
return false
end
return true
end
return self
end
-- detect and manage router ports
local function Ports()
local self = {}
local _ports = {}
local _sfp_ports = {}
local _ethernet_ports = {}
local help = selfs.Helper
local interface = selfs.Interfaces
local _ip_net = interface.get_ip_net_interfaces()
-- returns table of ethernet and sfp cards
local _cards = (function ()
local original = help.get_cards()
local ret = {}
for ci, c in pairs(original) do
if c:find("ethernet") or c:find("sfp") then
ret[ci] = c
end
end
return ret
end)()
-- returns table of all detected ports and seperate tables for only ethernet/sfp ports
local function _device_ports()
for _, k in pairs(_cards) do
local size = cli("status." .. k .. ".port.size")
local slot = k:match("(%d)$")
for p = 1, size do
_ports[slot .. "." .. p] = {card = k}
if k:find("ethernet") then
_ethernet_ports[slot .. "." .. p] = {card = k,
port = slot .. "." .. p,
link = cli("status." .. k .. ".port[" .. p .. "].link"),
net = cli("status." .. k .. ".port[" .. p .. "].port_net")}
else
_sfp_ports[slot .. "." .. p] = {card = k,
port = slot .. "." .. p,
link = cli("status." .. k .. ".port[" .. p .. "].link"),
net = cli("status." .. k .. ".port[" .. p .. "].port_net"),
has_module = cli("status." .. k .. ".port[" .. p .."].inserted")}
end
end
end
return _ports, _ethernet_ports, _sfp_ports
end
_device_ports()
-- returns table of detected ports
function self.get_ports()
return _ports
end
-- returns table of only ethernet ports
function self.get_ethernet_ports()
return _ethernet_ports
end
-- returns table of only sfp ports
function self.get_sfp_ports()
return _sfp_ports
end
-- updates all ports
function self.update_ports()
return _device_ports()
end
-- returns specific port (entry) from ports table
function self.get_port(port)
port = tostring(port)
if not _ports[port] then
lua_log("ERROR: Port " .. port .. " does not exist")
return false
end
if _ethernet_ports[port] then
return _ethernet_ports[port]
elseif _sfp_ports[port] then
return _sfp_ports[port]
end
end
-- detects link of ethernet/sfp port (up/down)
function self.get_port_link(port)
_device_ports()
local link
port = tostring(port)
-- checks if specific port is detected port on device
if not _ports[port] then
lua_log("ERROR: Port " .. port .. " does not exist")
return false, nil
end
-- returns link of port and updates entry
if _ethernet_ports[port] then
link = _ethernet_ports[port].link
else
link = _sfp_ports[port].link
end
return true, link
end
-- checks if specific sfp port has module inserted (true/false) and returns currently examined port
function self.get_port_has_sfp_module(port)
_device_ports()
port = tostring(port)
-- checks if sfp port exists
if not _sfp_ports[port] then
return false, nil
end
-- look up in sfp_port table to determine if port has a sfp module
if _sfp_ports[port].has_module == "yes" then
return true, port
end
return false, nil
end
-- returns interface used by given port (eg. Port: '1.1' uses 'net1')
function self.get_port_net(port)
_device_ports()
local net
port = tostring(port)
-- checks if specific port is a detected port on device
if not _ports[port] then
lua_log("ERROR: Port " .. " does not exist")
return false, nil
end
-- returns interface of port, '---' if no interface was assigned to port and updates table entry
if _ethernet_ports[port] then
net = _ethernet_ports[port].net
else
net = _sfp_ports[port].net
end
return true, net
end
-- sets desired interface to given port
function self.set_port_net(port, net)
port = tostring(port)
local slot, port_num = port:match("^(%d)%.(%d)$")
-- check if interface is a detected interface
if not _ip_net[net] and net ~= "---" then
lua_log("ERROR: IPnet Interface does not exist")
return false
end
-- returns both, assigned interface and edited port and updates net table entry
local ok, ipnet = pcall(cli,"interfaces.ethernet".. slot .. ".port" .. port_num .. "_active=" .. net)
if not ok then
ok, ipnet = pcall(cli,"interfaces.sfp" .. slot .. ".port" .. port_num .. "_active=" .. net)
if not ok then
lua_log("Port '" .. port .. "' does not exist")
return false
end
end
if _ethernet_ports[port] then
_ethernet_ports[port].net = ipnet
else
_sfp_ports[port].net = ipnet
end
return true
end
return self
end
-- send and handle recieved messages
local function Messages()
local self = {}
local _event_id
local _message
local _modem
local _sender
local interfaces = selfs.Interfaces
local con = selfs.Connectivity
local wans = interfaces.get_wan_interfaces()
-- sends mail to desired mail address with text and subject
function self.send_email(mail_address, text, subject)
cli("help.debug.email.recipient=" .. mail_address)
cli("help.debug.email.subject=" .. subject)
cli("help.debug.email.text=-----BEGIN TEXT-----" .. text .. "-----END TEXT-----")
local result = cli("help.debug.email.submit")
-- creates a log entry if email couldn't be sent
if result == "manual action failed" then
lua_log("ERROR: Email cannot be sent. Verify your contact: " .. mail_address)
return false
end
return true
end
-- sends sms to desired recipient with text messages through given modem
function self.send_sms(recipient, text, modem)
-- get the next best LTE modem found by Interface class
local default_modem = ( function()
for _, v in pairs(wans) do
if v.net:find("lte") then
local default = v.net
local ok, exit_code = con.lte_check_state(default)
if v.net:find("lte_serial") then
local lte, slot = v.net:match("^(lte)_serial(%d)$")
default = lte .. slot
end
if ok and exit_code >= 0 then
return default
end
end
end
end)()
modem = modem or default_modem
if not(modem or default_modem) then
lua_log("ERROR: No modem was found to send a SMS")
return false
end
cli("help.debug.sms.modem=" .. modem)
cli("help.debug.sms.recipient=" .. recipient)
cli("help.debug.sms.text=-----BEGIN TEXT-----" .. text .. "-----END TEXT-----")
local result = cli("help.debug.sms.submit")
-- creates a log entry if email couldn't be sent
if result == "manual action failed" then
lua_log("ERROR: SMS cannot be sent. Verify if phone number exists: " .. recipient)
return false
end
return true
end
-- sends notification based on an already configured contact in profile
function self.send_message(message)
cli("help.debug.message.message=" .. message)
local result = cli("help.debug.message.submit")
-- creates a log entry if email couldn't be sent
if result == "manual action failed" then
lua_log(string.format("ERROR: Message or notification could not be sent. Verify if %s exists or is set up", message))
return false
end
return true
end
-- store values of last message-event (ASCII trigger). Overwrites previous values
-- if function is used before an event occurs, all currently saved values that were extracted from a past event, will be nil
function self.message_event_extraction()
_event_id = (cli("events.info[event_id]"))
_message = (cli("events.info[message]"))
_modem = (cli("events.info[modem]"))
_sender = (cli("events.info[sender]"))
return _event_id, _message, _modem, _sender
end
-- return already extracted and store values from a message event
function self.get_extracted_event_info()
return _event_id, _message, _modem, _sender
end
return self
end
-- responsible for connectivity checks
local function Connectivity()
local self = {}
local interfaces = selfs.Interfaces
local _wans = interfaces.get_wan_interfaces()
local _ipnet = interfaces.get_ip_net_interfaces()
local _vpns = interfaces.get_vpn_interfaces()
-- checks validity of IP-Address. 0 == type of IP-Address is not a string, 1 == IPv4-Address, 2 == IPv6-Address, 3 == string (google.de) or invalid ip_address (999.999.999.999)
local function _get_ip_type(ip)
local r = {error = 0, ipv4 = 1, ipv6 = 2, string = 3}
if type(ip) ~= "string" then
lua_log("ERROR: IP is not type string")
return r.error
end
-- check for format 1.11.111.111 for ipv4
local chunks = {ip:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")}
if #chunks == 4 then
for _,v in pairs(chunks) do
if tonumber(v) > 255 then
lua_log("ERROR: Address" .. ip .. " is not a valid IPv4")
return r.string
end
end
return r.ipv4
end
-- check for ipv6 format, should be 8 'chunks' of numbers/letters
-- without leading/trailing chars
-- or fewer than 8 chunks, but with only one `::` group
local chunks = {ip:match("^"..(("([a-fA-F0-9]*):"):rep(8):gsub(":$","$")))}
if #chunks == 8 or #chunks < 8 and ip:match('::') and not ip:gsub("::","",1):match('::') then
for _,v in pairs(chunks) do
if #v > 0 and tonumber(v, 16) > 65535 then return r.string end
end
return r.ipv6
end
return r.string
end
-- make ping to an IP Addresss
local function ping(dest, net, wait_time, ping_time, ping_interval, num_of_pings, vx)
local exist, result, exit
-- if net and/or number of pings are set, check for validity and set parameter else use default empty value
if net and net ~= "" then
if net:find("lte_serial") then
local lte, slot = net:match("^(lte)_serial(%d)")
net = lte .. slot
end
if not(_ipnet[net] or _wans[net] or _vpns[net]) then
lua_log("ERROR: Source Interface " .. net .. " does not exist")
return false, nil
end
net = "-I " .. net .. " "
else
net = ""
end
if num_of_pings and num_of_pings ~= "" then
num_of_pings = tostring(num_of_pings)
if not num_of_pings:match("^%d+$") or tonumber(num_of_pings) < 1 then
lua_log("ERROR: Chosen Number of Pings is invalid")
return false, nil
end
num_of_pings = "-c" .. num_of_pings .. " "
else
num_of_pings = ""
end
if wait_time and wait_time ~= "" then
wait_time = tostring(wait_time)
if not wait_time:match("^%d+%.?%d*$") or tonumber(wait_time) < 0.002 then
lua_log("ERROR: Chosen time to wait for ping response is invalid")
return false, nil
end
wait_time = "-W" .. wait_time .. " "
else
wait_time = "-W5 "
end
if ping_time and ping_time ~= "" then
ping_time = tostring(ping_time)
if not ping_time:match("^%d+%.?%d*$") then
lua_log("ERROR: Chosen timeframe for pings is invalid")
return false,nil
end
ping_time = "-w"..ping_time.." "
else
ping_time = ""
end
if ping_interval and ping_interval ~= "" then
ping_interval = tostring(ping_interval)
if not ping_interval:match("^%d+%.?%d*$") or tonumber(ping_interval) < 0.002 then
lua_log("ERROR: Time between sending pings is invalid")
return false, nil
end
ping_interval = "-i"..ping_interval.." "
else
ping_interval = "-i0.9 "
end
-- checks for syntax of IP-Address and determines type of ICMP-Ping. versions == 6 for PING6, version == "" for PING4
local ip_type = _get_ip_type(dest)
if ip_type == 0 then
return false
elseif ip_type == 1 and vx ~= 4 then
return false
elseif ip_type == 2 and vx ~= 6 then
return false
elseif ip_type == 3 and (dest:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$") or dest:match("^"..(("([a-fA-F0-9]*):"):rep(8):gsub(":$","$")))) then
return false
end
vx = (vx == 4) and "" or 6
exist, result, exit = pcall(cli, "help.debug.tool=ping"..vx.." "..net..num_of_pings..wait_time..ping_time..ping_interval..dest)
-- if any other errors occur
if exit ~= 0 or not exist then
lua_log("Ping cannot be made: " .. result)
return false, nil
end
-- if more pings at once are made, that a certain threshold can't be undercut
local pl = result:match("(%d+)%% packet loss")
local loss = tonumber(pl)
local pong = {packet_loss = loss .. "%",
result_message = result,
exit_code = exit }
if loss < 40 then
return true, pong
end
lua_log("WARNING: Packet loss is at " .. loss .. "%")
return false, pong
end
-- ping a v4 address
-- net parameter(OPTIONAL): ping from given interface
-- num_of_pings parameter(OPTIONAL): number of pings
function self.ping(dest, net, wait_time, ping_time, ping_interval, num_of_pings)
local ok, result = ping(dest, net, wait_time, ping_time, ping_interval, num_of_pings, 4)
return ok, result
end
-- ping a v6 address
-- same optional parameters as above
function self.ping6(dest, net, wait_time, ping_time, ping_interval, num_of_pings)
local ok, result = ping(dest, net, wait_time, ping_time, ping_interval, num_of_pings, 6)
return ok, result
end
-- checks connectivity of LTE Modem and returns true/false if usable and state of modem
function self.lte_check_state(modem)
local state_handler = {
["Turned off"] = 0,
["Turned on"] = 1,
["SIM interface down"] = 2,
["Logged out"] = 3,
["Logging in"] = 4,
["Logged in"] = 5, -- modem can at least send sms
["Offline"] = 6,
["Online"] = 7 -- modem has internet connection
}
-- checks if modem parameter is not only a string (in case of a misuse) but also a valid lte modem
if type(modem) ~= 'string' then
lua_log("ERROR: Chosen Modem is invalid")
return false, nil
elseif not modem:find("lte") then
lua_log("ERROR: Cant check for LTE functionality because " .. modem .. " has no LTE modem")
return false, nil
elseif not _wans[modem] then
lua_log("ERROR: " .. modem .. " does not exist")
return false, nil
end
local ok , state = pcall(cli, "status.".. modem .. ".state")
if not ok then
lte, slot = modem:match("^(.-)(%d)")
modem = lte .. "_serial" .. slot
state = cli("status.".. modem .. ".state")
end
if state_handler[state] == 7 then
lua_log("Modem has state '" .. state .. "': Internet connection")
return true, 1
elseif state_handler[state] == 6 or state_handler[state] == 5 then
lua_log("Modem has state '" .. state .. "': Connected to provider " .. cli("status." .. modem .. ".provider_name") .. " - >able to send SMS")
return true, 0
else
lua_log("WARNING: Modem has state '" .. state .. "': not connected to a provider -> unable to send sms or connect to internet")
return true, -1
end
end
-- restarts lte modem
function self.restart_modem(modem)
if modem:find("serial") then
lte, slot = modem:match("^(lte)_serial(%d)")
modem = lte .. slot
end
if not _wans[modem] then
lua_log("ERROR: " .. modem .. " is not a modem")
return false
end
cli("help.debug.modem_state.name=" .. modem)
cli("help.debug.modem_state.state_change=turn_off")
cli("help.debug.modem_state.submit")
sleep(5)
cli("help.debug.modem_state.name=" .. modem)
cli("help.debug.modem_state.state_change=turn_on")
cli("help.debug.modem_state.submit")
sleep(5)
cli("help.debug.modem_state.name=" .. modem)
cli("help.debug.modem_state.state_change=log_in")
cli("help.debug.modem_state.submit")
return true
end
return self
end
-- scans and organizes I/Os of device
local function IOs()
local self = {}
local _outputs = {}
local _inputs = {}
local help = selfs.Helper
-- remove all Ethernet cards, because they don't have any IOs
local _cards = (function ()
local original = help.get_cards()
local ret = {}
for ci, c in pairs(original) do
if not c:find("ethernet") then
ret[ci] = c
end
end
return ret
end)()
local _device_info = help.get_device_type()
local _cli_table = help.cli_specifics_into_table
-- helper function to add IOs
-- parameter 'on_card' has both the MR-Card and corresponding slot (eg. ethernet1, adio5)
local function _add_io(table, io, analog_digital, status, on_card)
table[io] = {index = io,
status = status,
analog_digital = analog_digital,
on_card = on_card}
end
-- find I/Os on MRX
local function _ios_mrx()
for _, card in pairs (_cards) do
local slot = card:match("(%d)$")
-- DSL is special case because of different CLI syntax
if card:find("dsl") then
_add_io(_inputs, slot .. ".1", cli("status." .. card .. ".status.input_1"), card)
_add_io(_inputs, slot .. ".2", cli("status." .. card .. ".status.input_2"), card)
else
-- process the rest of the found cards
local cmds = _cli_table(cli("status." .. card))
for _, k in ipairs(cmds) do
local io_type, io, io_num = k:match("([%a+_]*)(input)_(%d)")
if not io then
io_type, io, io_num = k:match("([%a+_]*)(output)_(%d)")
end
if io then
local io_table = (io == "input") and _inputs or _outputs
local io_index = (io_type == "analog_") and slot .. "." .. io_num .. "a" or slot .. "." .. io_num
local io_state = cli("status." .. card .. "." .. io_type .. io .. "_" .. io_num)
local io_types = (io_type == "analog_") and "analog" or (io_type == "digital_") and "digital" or ""
_add_io(io_table, io_index, io_types, io_state, card)
end
end
end
end
return _inputs, _outputs
end
-- find I/Os of MIRO
local function _ios_miro()
local io_mode = cli("status.io2.direction")
-- MIRO has programmable I/O, if I/O switches state, entry in other table will be deleted
if io_mode == "in" then
if _outputs["2.1"] then
_outputs["2.1"] = nil
end
_add_io(_inputs, "2.1", cli("status.io2.state"), "io2")
return _inputs
else
-- same applies to the case above with input changed to output
if _inputs["2.1"] then
_inputs["2.1"] = nil
end
_add_io(_outputs, "2.1", cli("status.io2.state"), "io2")
return _outputs
end
end
-- find IOs on SCR or ECR
local function _ios_scr_ecr()
_add_io(_inputs, "3.1", cli("status.io3.input_1"), "io3")
_add_io(_inputs, "3.2", cli("status.io3.input_2"), "io3")
_add_io(_outputs, "3.1", cli("status.io3.output_1"), "io3")
_add_io(_outputs, "3.2", cli("status.io3.output_2"), "io3")
return _inputs, _outputs
end