AlLoRa 1.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- AlLoRa-1.1.0/AlLoRa/Connectors/Connector.py +111 -0
- AlLoRa-1.1.0/AlLoRa/Connectors/LoPy4_connector.py +76 -0
- AlLoRa-1.1.0/AlLoRa/Connectors/SX127x_connector.py +63 -0
- AlLoRa-1.1.0/AlLoRa/Connectors/Serial_connector.py +49 -0
- AlLoRa-1.1.0/AlLoRa/Connectors/Wifi_connector.py +67 -0
- AlLoRa-1.1.0/AlLoRa/Connectors/__init__.py +0 -0
- AlLoRa-1.1.0/AlLoRa/DataSource.py +115 -0
- AlLoRa-1.1.0/AlLoRa/Digital_Endpoint.py +105 -0
- AlLoRa-1.1.0/AlLoRa/File.py +113 -0
- AlLoRa-1.1.0/AlLoRa/Interfaces/Interface.py +18 -0
- AlLoRa-1.1.0/AlLoRa/Interfaces/Serial_interface.py +62 -0
- AlLoRa-1.1.0/AlLoRa/Interfaces/Wifi_client.py +109 -0
- AlLoRa-1.1.0/AlLoRa/Interfaces/Wifi_hotspot.py +73 -0
- AlLoRa-1.1.0/AlLoRa/Interfaces/__init__.py +0 -0
- AlLoRa-1.1.0/AlLoRa/Nodes/Adapter.py +44 -0
- AlLoRa-1.1.0/AlLoRa/Nodes/Gateway.py +56 -0
- AlLoRa-1.1.0/AlLoRa/Nodes/Node.py +107 -0
- AlLoRa-1.1.0/AlLoRa/Nodes/Receiver.py +141 -0
- AlLoRa-1.1.0/AlLoRa/Nodes/Sender.py +212 -0
- AlLoRa-1.1.0/AlLoRa/Nodes/__init__.py +0 -0
- AlLoRa-1.1.0/AlLoRa/Packet.py +339 -0
- AlLoRa-1.1.0/AlLoRa/__init__.py +0 -0
- AlLoRa-1.1.0/AlLoRa.egg-info/PKG-INFO +634 -0
- AlLoRa-1.1.0/AlLoRa.egg-info/SOURCES.txt +45 -0
- AlLoRa-1.1.0/AlLoRa.egg-info/dependency_links.txt +1 -0
- AlLoRa-1.1.0/AlLoRa.egg-info/top_level.txt +2 -0
- AlLoRa-1.1.0/LICENSE +674 -0
- AlLoRa-1.1.0/PKG-INFO +634 -0
- AlLoRa-1.1.0/README.md +615 -0
- AlLoRa-1.1.0/examples/Adapters/Serial_adapter/__init__.py +0 -0
- AlLoRa-1.1.0/examples/Adapters/Serial_adapter/main.py +15 -0
- AlLoRa-1.1.0/examples/Adapters/WiFi_adapters/__init__.py +0 -0
- AlLoRa-1.1.0/examples/Adapters/__init__.py +0 -0
- AlLoRa-1.1.0/examples/LoPyReceiver/__init__.py +0 -0
- AlLoRa-1.1.0/examples/LoPyReceiver/boot.py +11 -0
- AlLoRa-1.1.0/examples/LoPyReceiver/main.py +27 -0
- AlLoRa-1.1.0/examples/LoPySender/__init__.py +0 -0
- AlLoRa-1.1.0/examples/LoPySender/boot.py +11 -0
- AlLoRa-1.1.0/examples/LoPySender/main.py +64 -0
- AlLoRa-1.1.0/examples/RaspberryGateway/Serial_Gateway/__init__.py +0 -0
- AlLoRa-1.1.0/examples/RaspberryGateway/Serial_Gateway/main.py +17 -0
- AlLoRa-1.1.0/examples/RaspberryGateway/Wifi_Gateway/__init__.py +0 -0
- AlLoRa-1.1.0/examples/RaspberryGateway/Wifi_Gateway/main.py +16 -0
- AlLoRa-1.1.0/examples/RaspberryGateway/__init__.py +0 -0
- AlLoRa-1.1.0/examples/__init__.py +0 -0
- AlLoRa-1.1.0/setup.cfg +4 -0
- AlLoRa-1.1.0/setup.py +35 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from AlLoRa.Packet import Packet
|
|
2
|
+
try:
|
|
3
|
+
from utime import sleep, ticks_ms as time
|
|
4
|
+
from uos import urandom
|
|
5
|
+
import gc
|
|
6
|
+
except:
|
|
7
|
+
from time import sleep, time
|
|
8
|
+
from os import urandom
|
|
9
|
+
|
|
10
|
+
class Connector:
|
|
11
|
+
MAX_LENGTH_MESSAGE = 255
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
self.MAC = "00000000"
|
|
15
|
+
|
|
16
|
+
def config(self, name="N", frequency = 868, sf=7, mesh_mode=False, debug=False, min_timeout = 0.5, max_timeout = 6):
|
|
17
|
+
self.name = name
|
|
18
|
+
self.frequency = frequency
|
|
19
|
+
self.sf = sf
|
|
20
|
+
self.mesh_mode = mesh_mode
|
|
21
|
+
|
|
22
|
+
self.debug = debug
|
|
23
|
+
|
|
24
|
+
self.min_timeout = min_timeout
|
|
25
|
+
self.max_timeout = max_timeout
|
|
26
|
+
self.adaptive_timeout = self.min_timeout
|
|
27
|
+
self.backup_timeout = self.adaptive_timeout
|
|
28
|
+
|
|
29
|
+
self.sf_backup = self.sf
|
|
30
|
+
|
|
31
|
+
def backup_config(self):
|
|
32
|
+
return {"freq": self.frequency,
|
|
33
|
+
"sf": self.sf,
|
|
34
|
+
"min_timeout": self.min_timeout,
|
|
35
|
+
"max_timeout": self.max_timeout
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
def get_mac(self):
|
|
39
|
+
return self.MAC
|
|
40
|
+
|
|
41
|
+
def set_sf(self, sf):
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
def backup_sf(self):
|
|
45
|
+
self.sf_backup = self.sf
|
|
46
|
+
|
|
47
|
+
def restore_sf(self):
|
|
48
|
+
self.set_sf(self.sf_backup)
|
|
49
|
+
|
|
50
|
+
def set_mesh_mode(self, mesh_mode=False):
|
|
51
|
+
self.mesh_mode = mesh_mode
|
|
52
|
+
|
|
53
|
+
def send(self, packet: Packet):
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
def recv(self, focus_time=12):
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
def send_and_wait_response(self, packet):
|
|
60
|
+
packet.set_source(self.get_mac()) # Adding mac address to packet
|
|
61
|
+
focus_time = self.adaptive_timeout
|
|
62
|
+
send_success = self.send(packet)
|
|
63
|
+
#if not send_success:
|
|
64
|
+
#return None
|
|
65
|
+
|
|
66
|
+
while focus_time > 0:
|
|
67
|
+
t0 = time()
|
|
68
|
+
received_data = self.recv(focus_time)
|
|
69
|
+
td = (time() - t0)/1000
|
|
70
|
+
if not received_data:
|
|
71
|
+
random_factor = int.from_bytes(urandom(2), "little") / 2**16
|
|
72
|
+
self.adaptive_timeout = min(self.adaptive_timeout * (1 + random_factor),
|
|
73
|
+
self.max_timeout) #random exponential backoff.
|
|
74
|
+
gc.collect()
|
|
75
|
+
return None
|
|
76
|
+
response_packet = Packet(self.mesh_mode)
|
|
77
|
+
if self.debug:
|
|
78
|
+
self.signal_estimation()
|
|
79
|
+
print("WAIT_RESPONSE({}) || sender_reply: {}".format(self.adaptive_timeout, received_data))
|
|
80
|
+
try:
|
|
81
|
+
response_packet.load(received_data)
|
|
82
|
+
if response_packet.get_source() == packet.get_destination() and response_packet.get_destination() == self.get_mac():
|
|
83
|
+
if len(received_data) > response_packet.HEADER_SIZE + 60: # Hardcoded for only chunks
|
|
84
|
+
self.adaptive_timeout = max(self.adaptive_timeout * 0.8 + td * 0.21, self.min_timeout)
|
|
85
|
+
if response_packet.get_debug_hops():
|
|
86
|
+
response_packet.add_hop(self.name, self.get_rssi(), 0)
|
|
87
|
+
if response_packet.get_change_sf():
|
|
88
|
+
print("OK and changing sf")
|
|
89
|
+
new_sf = int(response_packet.get_payload().decode().split('"')[1])
|
|
90
|
+
print(new_sf)
|
|
91
|
+
self.set_sf(new_sf)
|
|
92
|
+
gc.collect()
|
|
93
|
+
return response_packet
|
|
94
|
+
except Exception as e:
|
|
95
|
+
print("Corrupted packet received", e, received_data)
|
|
96
|
+
focus_time = self.adaptive_timeout - td
|
|
97
|
+
|
|
98
|
+
""" This function returns the RSSI of the last received packet"""
|
|
99
|
+
def get_rssi(self):
|
|
100
|
+
return 0
|
|
101
|
+
|
|
102
|
+
def signal_estimation(self):
|
|
103
|
+
percentage = 0
|
|
104
|
+
rssi = self.get_rssi()
|
|
105
|
+
if (rssi >= -50):
|
|
106
|
+
percentage = 100
|
|
107
|
+
elif (rssi <= -50) and (rssi >= -100):
|
|
108
|
+
percentage = 2 * (rssi + 100)
|
|
109
|
+
elif (rssi < 100):
|
|
110
|
+
percentage = 0
|
|
111
|
+
print('SIGNAL STRENGTH', percentage, '%')
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from time import sleep
|
|
2
|
+
|
|
3
|
+
from AlLoRa.Packet import Packet
|
|
4
|
+
from AlLoRa.Connectors.Connector import Connector
|
|
5
|
+
from network import LoRa
|
|
6
|
+
import socket
|
|
7
|
+
import binascii
|
|
8
|
+
|
|
9
|
+
import pycom
|
|
10
|
+
|
|
11
|
+
class LoPy4_connector(Connector):
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
super().__init__()
|
|
15
|
+
self.MAC = binascii.hexlify(LoRa().mac()).decode('utf-8')[-8:]
|
|
16
|
+
|
|
17
|
+
def config(self, name = "L", frequency = 868, sf=7,
|
|
18
|
+
mesh_mode=False, debug=False, min_timeout = 0.5, max_timeout = 6): #max_timeout = 100
|
|
19
|
+
super().config(name, frequency, sf, mesh_mode, debug, min_timeout, max_timeout)
|
|
20
|
+
self.lora = LoRa(mode=LoRa.LORA, frequency=self.frequency*1000000,
|
|
21
|
+
region=LoRa.EU868, sf = self.sf)
|
|
22
|
+
self.lora_socket = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
|
|
23
|
+
|
|
24
|
+
def set_sf(self, sf):
|
|
25
|
+
if self.sf != sf:
|
|
26
|
+
self.lora.sf(sf)
|
|
27
|
+
self.sf = sf
|
|
28
|
+
if self.debug:
|
|
29
|
+
print("SF Changed to: ", self.sf)
|
|
30
|
+
|
|
31
|
+
def get_stats(self):
|
|
32
|
+
stats = self.lora.stats()
|
|
33
|
+
if self.debug:
|
|
34
|
+
print("rx_timestamp {0}, rssi {1}, snr {2}, sftx {3}, sfrx {4}, tx_trials {5}, tx_power {6}, tx_time_on_air {7}, tx_counter {8}, tx_frequency {9}".format(stats[0],
|
|
35
|
+
stats[1], stats[2], stats[3], stats[4], stats[5], stats[6], stats[7], stats[8], stats[9]))
|
|
36
|
+
return stats
|
|
37
|
+
|
|
38
|
+
def get_rssi(self):
|
|
39
|
+
return self.lora.stats()[1]
|
|
40
|
+
|
|
41
|
+
def send(self, packet):
|
|
42
|
+
if packet.get_length() <= Connector.MAX_LENGTH_MESSAGE:
|
|
43
|
+
if self.debug:
|
|
44
|
+
print("SEND_PACKET(SF: {}) || packet: {}".format(self.sf, packet.get_content()))
|
|
45
|
+
if packet.get_mesh():
|
|
46
|
+
pycom.rgbled(0xb19cd8) # purple
|
|
47
|
+
else:
|
|
48
|
+
pycom.rgbled(0x007f00) # green
|
|
49
|
+
try:
|
|
50
|
+
self.lora_socket.send(packet.get_content()) #.encode()
|
|
51
|
+
if self.debug:
|
|
52
|
+
pycom.rgbled(0) # off
|
|
53
|
+
return True
|
|
54
|
+
except:
|
|
55
|
+
if self.debug:
|
|
56
|
+
pycom.rgbled(0) # off
|
|
57
|
+
return False
|
|
58
|
+
else:
|
|
59
|
+
if self.debug:
|
|
60
|
+
print("Error: Packet too big")
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
def recv(self, focus_time=12):
|
|
64
|
+
try:
|
|
65
|
+
self.lora_socket.settimeout(focus_time)
|
|
66
|
+
data = self.lora_socket.recv(Connector.MAX_LENGTH_MESSAGE)
|
|
67
|
+
self.lora_socket.setblocking(False)
|
|
68
|
+
if self.debug:
|
|
69
|
+
self.get_stats()
|
|
70
|
+
return data
|
|
71
|
+
except Exception as e:
|
|
72
|
+
if self.debug:
|
|
73
|
+
print("Error receiving: ", e)
|
|
74
|
+
|
|
75
|
+
def send_and_wait_response(self, packet):
|
|
76
|
+
return super().send_and_wait_response(packet)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import ubinascii
|
|
3
|
+
import network
|
|
4
|
+
|
|
5
|
+
from AlLoRa.Packet import Packet
|
|
6
|
+
from AlLoRa.Connectors.Connector import Connector
|
|
7
|
+
|
|
8
|
+
# Requires PyLora_SX127x_extensions to be installed
|
|
9
|
+
# https://github.com/GRCDEV/PyLora_SX127x_extensions
|
|
10
|
+
from PyLora_SX127x_extensions.pyLora import pyLora
|
|
11
|
+
|
|
12
|
+
class SX127x_connector(Connector):
|
|
13
|
+
|
|
14
|
+
def __init__(self): #max_timeout = 10
|
|
15
|
+
super().__init__()
|
|
16
|
+
|
|
17
|
+
wlan_sta = network.WLAN(network.STA_IF)
|
|
18
|
+
wlan_sta.active(True)
|
|
19
|
+
wlan_mac = wlan_sta.config('mac')
|
|
20
|
+
self.MAC = ubinascii.hexlify(wlan_mac).decode()[-8:]
|
|
21
|
+
wlan_sta.active(False)
|
|
22
|
+
|
|
23
|
+
def config(self, name = "S", frequency = 868, sf=7, mesh_mode=False, debug=False, min_timeout = 0.5, max_timeout = 6): #max_timeout = 10
|
|
24
|
+
super().config(name, frequency, sf, mesh_mode, debug, min_timeout, max_timeout)
|
|
25
|
+
self.lora = pyLora(freq = self.frequency,
|
|
26
|
+
sf=self.sf, verbose= self.debug)
|
|
27
|
+
self.lora.setblocking(False)
|
|
28
|
+
|
|
29
|
+
def set_sf(self, sf):
|
|
30
|
+
if self.sf != sf:
|
|
31
|
+
self.lora.sf(sf)
|
|
32
|
+
self.sf = sf
|
|
33
|
+
if self.debug:
|
|
34
|
+
print("SF Changed to: ", self.sf)
|
|
35
|
+
|
|
36
|
+
def get_rssi(self):
|
|
37
|
+
return self.lora.get_rssi()
|
|
38
|
+
|
|
39
|
+
def send(self, packet):
|
|
40
|
+
if self.debug:
|
|
41
|
+
print("SEND_PACKET() || packet: {}".format(packet.get_content()))
|
|
42
|
+
if packet.get_length() <= Connector.MAX_LENGTH_MESSAGE:
|
|
43
|
+
try:
|
|
44
|
+
self.lora.setblocking(True)
|
|
45
|
+
self.lora.send(packet.get_content()) # .encode()
|
|
46
|
+
self.lora.setblocking(False)
|
|
47
|
+
return True
|
|
48
|
+
except:
|
|
49
|
+
self.lora.setblocking(False)
|
|
50
|
+
return False
|
|
51
|
+
else:
|
|
52
|
+
if self.debug:
|
|
53
|
+
print("Error: Packet too big")
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
def recv(self, focus_time=12):
|
|
57
|
+
try:
|
|
58
|
+
self.lora.settimeout(focus_time)
|
|
59
|
+
data = self.lora.recv(Connector.MAX_LENGTH_MESSAGE)
|
|
60
|
+
return data
|
|
61
|
+
except:
|
|
62
|
+
if self.debug:
|
|
63
|
+
print("nothing received or error")
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import serial, struct
|
|
2
|
+
from time import sleep, time
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
from AlLoRa.Packet import Packet
|
|
6
|
+
from AlLoRa.Connectors.Connector import Connector
|
|
7
|
+
|
|
8
|
+
class Serial_connector(Connector):
|
|
9
|
+
|
|
10
|
+
def __init__(self, serial_port = "/dev/ttyAMA3", baud = 9600, timeout = 1):
|
|
11
|
+
super().__init__()
|
|
12
|
+
self.SERIAL_PORT = serial_port
|
|
13
|
+
self.BAUD_RATE = baud
|
|
14
|
+
self.timeout = timeout
|
|
15
|
+
self.serial = serial.Serial(self.SERIAL_PORT, self.BAUD_RATE, timeout=self.timeout)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def send_and_wait_response(self, packet: Packet) -> Packet:
|
|
19
|
+
if self.debug:
|
|
20
|
+
print("send and wait")
|
|
21
|
+
retry = True
|
|
22
|
+
max_retries = 1
|
|
23
|
+
response_packet = Packet(self.mesh_mode)
|
|
24
|
+
while max_retries > 0 and retry:
|
|
25
|
+
try:
|
|
26
|
+
content = packet.get_content()
|
|
27
|
+
if self.debug:
|
|
28
|
+
print("Sending: ", content)
|
|
29
|
+
|
|
30
|
+
self.serial.write(content)
|
|
31
|
+
sleep(1)
|
|
32
|
+
received_data = self.serial.read(255)
|
|
33
|
+
|
|
34
|
+
if received_data:
|
|
35
|
+
response_packet = Packet(self.mesh_mode)
|
|
36
|
+
check = response_packet.load(received_data)
|
|
37
|
+
if check:
|
|
38
|
+
if self.debug:
|
|
39
|
+
print("Receiving: ", response_packet.get_content())
|
|
40
|
+
retry = False
|
|
41
|
+
|
|
42
|
+
except Exception as e:
|
|
43
|
+
retry = True
|
|
44
|
+
if self.debug:
|
|
45
|
+
print("Error S&W: ", e)
|
|
46
|
+
finally:
|
|
47
|
+
max_retries -= 1
|
|
48
|
+
|
|
49
|
+
return response_packet
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import socket
|
|
2
|
+
import json
|
|
3
|
+
import select
|
|
4
|
+
from time import sleep, time
|
|
5
|
+
|
|
6
|
+
from AlLoRa.Packet import Packet
|
|
7
|
+
from AlLoRa.Connectors.Connector import Connector
|
|
8
|
+
|
|
9
|
+
class WiFi_connector(Connector):
|
|
10
|
+
|
|
11
|
+
def __init__(self, RECEIVER_API_HOST, RECEIVER_API_PORT, SOCKET_TIMEOUT=10,
|
|
12
|
+
SOCKET_RECV_SIZE=10000, PACKET_RETRY_SLEEP=0.5, logger_error=None):
|
|
13
|
+
super().__init__()
|
|
14
|
+
self.SOCKET_TIMEOUT = SOCKET_TIMEOUT
|
|
15
|
+
self.RECEIVER_API_HOST = RECEIVER_API_HOST
|
|
16
|
+
self.RECEIVER_API_PORT = RECEIVER_API_PORT
|
|
17
|
+
self.SOCKET_RECV_SIZE = SOCKET_RECV_SIZE
|
|
18
|
+
self.logger_error = logger_error
|
|
19
|
+
self.PACKET_RETRY_SLEEP = PACKET_RETRY_SLEEP
|
|
20
|
+
|
|
21
|
+
def send_and_wait_response(self, packet: Packet) -> Packet:
|
|
22
|
+
json_response = None
|
|
23
|
+
retry = True
|
|
24
|
+
max_retries = 1
|
|
25
|
+
response_packet = Packet(self.mesh_mode)
|
|
26
|
+
while max_retries > 0 and retry:
|
|
27
|
+
try:
|
|
28
|
+
s = socket.socket()
|
|
29
|
+
s.setblocking(True)
|
|
30
|
+
addr = socket.getaddrinfo(self.RECEIVER_API_HOST, self.RECEIVER_API_PORT)[0][-1]
|
|
31
|
+
s.settimeout(self.SOCKET_TIMEOUT)
|
|
32
|
+
s.connect(addr)
|
|
33
|
+
|
|
34
|
+
content_str = packet.get_dict()
|
|
35
|
+
content = json.dumps({"packet": content_str})
|
|
36
|
+
|
|
37
|
+
httpreq = 'POST {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\nAccept: */*\r\nContent-Type: application/json\r\nContent-Length: {}\r\n\r\n{}'.format(
|
|
38
|
+
"/send-packet", self.RECEIVER_API_HOST, len(content), content).encode('utf-8')
|
|
39
|
+
|
|
40
|
+
ready_to_read, ready_to_write, in_error = select.select([],
|
|
41
|
+
[s],
|
|
42
|
+
[],
|
|
43
|
+
15)
|
|
44
|
+
s.send(httpreq)
|
|
45
|
+
ready_to_read, ready_to_write, in_error = select.select([s],
|
|
46
|
+
[],
|
|
47
|
+
[s],
|
|
48
|
+
15)
|
|
49
|
+
response = s.recv(self.SOCKET_RECV_SIZE)
|
|
50
|
+
|
|
51
|
+
retry = False
|
|
52
|
+
try:
|
|
53
|
+
extracted_response = response.decode('utf-8').split('\r\n\r\n')[1]
|
|
54
|
+
json_response = json.loads(extracted_response)
|
|
55
|
+
response_packet.load_dict(json_response['response_packet'])
|
|
56
|
+
except Exception as e:
|
|
57
|
+
#print("Error in load: ", e)
|
|
58
|
+
pass # It fails when response is empty
|
|
59
|
+
|
|
60
|
+
except Exception as e:
|
|
61
|
+
#self.logger_error.error("Allowed Exception (Network connection was interrupted by some reason, but will keep trying to reconnect): {}".format(e))
|
|
62
|
+
sleep(self.PACKET_RETRY_SLEEP)
|
|
63
|
+
retry = True
|
|
64
|
+
finally:
|
|
65
|
+
max_retries -= 1
|
|
66
|
+
|
|
67
|
+
return response_packet
|
|
File without changes
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import _thread
|
|
2
|
+
import utime
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from AlLoRa.File import CTP_File
|
|
6
|
+
|
|
7
|
+
# Do not instanciate this class as pretends to be an abstract one
|
|
8
|
+
class DataSource:
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def __init__(self, file_chunk_size: int, file_queue_size=25, sleep_between_readings=60):
|
|
12
|
+
|
|
13
|
+
self.STOP_THREAD = True
|
|
14
|
+
self.IS_STARTED = False
|
|
15
|
+
|
|
16
|
+
self.file_queue = []
|
|
17
|
+
self.file_queue_size = file_queue_size
|
|
18
|
+
self.file_chunk_size = file_chunk_size
|
|
19
|
+
|
|
20
|
+
self.SECONDS_BETWEEN_READINGS = sleep_between_readings
|
|
21
|
+
|
|
22
|
+
self.backup_file = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_file_chunk_size(self):
|
|
26
|
+
return self.file_chunk_size
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def add_to_queue(self, file: CTP_File):
|
|
30
|
+
if len(self.file_queue) >= self.file_queue_size:
|
|
31
|
+
self.file_queue.pop(0)
|
|
32
|
+
repeated = False
|
|
33
|
+
for f in self.file_queue:
|
|
34
|
+
if f.get_name() == file.get_name():
|
|
35
|
+
repeated = True
|
|
36
|
+
break
|
|
37
|
+
if repeated is False:
|
|
38
|
+
self.file_queue.append(file)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def read(self):
|
|
42
|
+
while self.STOP_THREAD is False:
|
|
43
|
+
try:
|
|
44
|
+
file = self.read_datasource()
|
|
45
|
+
if file is not None:
|
|
46
|
+
self.add_to_queue(file=file)
|
|
47
|
+
else:
|
|
48
|
+
print("skipped file, it is None")
|
|
49
|
+
utime.sleep(self.SECONDS_BETWEEN_READINGS)
|
|
50
|
+
except KeyboardInterrupt as e:
|
|
51
|
+
self.stop()
|
|
52
|
+
self.IS_STARTED = False
|
|
53
|
+
print(self.STOP_THREAD)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def read_datasource(self) -> CTP_File:
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def prepare(self):
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
def start(self):
|
|
64
|
+
self.prepare()
|
|
65
|
+
self.STOP_THREAD = False
|
|
66
|
+
self.IS_STARTED = True
|
|
67
|
+
_thread.start_new_thread(self.read, ())
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def stop(self):
|
|
71
|
+
self.STOP_THREAD = True
|
|
72
|
+
print(self.STOP_THREAD)
|
|
73
|
+
|
|
74
|
+
def is_started(self):
|
|
75
|
+
return self.IS_STARTED
|
|
76
|
+
|
|
77
|
+
'''
|
|
78
|
+
Everytime this function is called, it assumes the file is already consumed, so a deleting is performed.
|
|
79
|
+
'''
|
|
80
|
+
def get_next_file(self):
|
|
81
|
+
try:
|
|
82
|
+
backup_file = self.file_queue.pop(0)
|
|
83
|
+
self.backup(file=backup_file)
|
|
84
|
+
return backup_file
|
|
85
|
+
except IndexError as e:
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def get_backup(self):
|
|
90
|
+
try:
|
|
91
|
+
filename = ""
|
|
92
|
+
with open("./filename-backup.txt", "r") as f:
|
|
93
|
+
filename = f.read()
|
|
94
|
+
|
|
95
|
+
content = None
|
|
96
|
+
with open("./content-backup", "r") as f:
|
|
97
|
+
content = f.read()
|
|
98
|
+
rescued_file = CTP_File(name='{}'.format(filename), content=bytearray(content), chunk_size=self.file_chunk_size)
|
|
99
|
+
return rescued_file
|
|
100
|
+
except OSError as e:
|
|
101
|
+
print("The backup could not be restored", e)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def backup(self, file: CTP_File):
|
|
105
|
+
try:
|
|
106
|
+
os.remove("./filename-backup.txt")
|
|
107
|
+
os.remove("./content-backup")
|
|
108
|
+
except OSError:
|
|
109
|
+
pass
|
|
110
|
+
|
|
111
|
+
with open("./filename-backup.txt", "w") as f:
|
|
112
|
+
f.write(file.get_name())
|
|
113
|
+
|
|
114
|
+
with open("./content-backup", "w") as f:
|
|
115
|
+
f.write(file.get_content())
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
from AlLoRa.File import CTP_File
|
|
2
|
+
|
|
3
|
+
class Digital_Endpoint:
|
|
4
|
+
|
|
5
|
+
REQUEST_DATA_STATE = "REQUEST_DATA_STATE"
|
|
6
|
+
PROCESS_CHUNK_STATE = "PROCESS_CHUNK_STATE"
|
|
7
|
+
OK = "OK"
|
|
8
|
+
|
|
9
|
+
def __init__(self, name: str, mac_address: str, active: bool = True, sleep_mesh: bool = True,
|
|
10
|
+
MAX_RETRANSMISSIONS_BEFORE_MESH: int = 10):
|
|
11
|
+
self.name = name
|
|
12
|
+
self.mac_address = mac_address[-8:]
|
|
13
|
+
self.active = active
|
|
14
|
+
self.current_file = None
|
|
15
|
+
|
|
16
|
+
self.state = Digital_Endpoint.OK
|
|
17
|
+
|
|
18
|
+
self.mesh = False
|
|
19
|
+
self.sleep_mesh = sleep_mesh
|
|
20
|
+
self.retransmission_counter = 0
|
|
21
|
+
self.MAX_RETRANSMISSIONS_BEFORE_MESH = MAX_RETRANSMISSIONS_BEFORE_MESH
|
|
22
|
+
|
|
23
|
+
def get_name(self):
|
|
24
|
+
return self.name
|
|
25
|
+
|
|
26
|
+
def get_mac_address(self):
|
|
27
|
+
return self.mac_address
|
|
28
|
+
|
|
29
|
+
def get_mesh(self):
|
|
30
|
+
return self.mesh
|
|
31
|
+
|
|
32
|
+
def is_active(self):
|
|
33
|
+
return self.active
|
|
34
|
+
|
|
35
|
+
def enable_mesh(self):
|
|
36
|
+
self.mesh = True
|
|
37
|
+
print("Node {}: ENABLING MESH".format(self.name))
|
|
38
|
+
|
|
39
|
+
def disable_mesh(self):
|
|
40
|
+
self.mesh = False
|
|
41
|
+
self.retransmission_counter = 0
|
|
42
|
+
print("Node {}: DISABLING MESH".format(self.name))
|
|
43
|
+
|
|
44
|
+
def get_sleep(self):
|
|
45
|
+
return self.sleep_mesh
|
|
46
|
+
|
|
47
|
+
def count_retransmission(self):
|
|
48
|
+
if not self.get_mesh():
|
|
49
|
+
self.retransmission_counter += 1
|
|
50
|
+
if self.retransmission_counter >= self.MAX_RETRANSMISSIONS_BEFORE_MESH:
|
|
51
|
+
self.enable_mesh()
|
|
52
|
+
|
|
53
|
+
def reset_retransmission_counter(self, hop): #packet
|
|
54
|
+
if not self.get_mesh(): # If mesh mode is deactivated and I receive a message from this node
|
|
55
|
+
self.retransmission_counter = 0 # Reset counter, going well...
|
|
56
|
+
else:
|
|
57
|
+
if not hop:
|
|
58
|
+
self.disable_mesh()
|
|
59
|
+
|
|
60
|
+
def set_current_file(self, file: CTP_File):
|
|
61
|
+
self.current_file = file
|
|
62
|
+
|
|
63
|
+
def get_current_file(self):
|
|
64
|
+
return self.current_file
|
|
65
|
+
|
|
66
|
+
def connected(self, ok, hop, mesh_mode):
|
|
67
|
+
if ok:
|
|
68
|
+
if mesh_mode:
|
|
69
|
+
self.reset_retransmission_counter(hop)
|
|
70
|
+
self.state = Digital_Endpoint.REQUEST_DATA_STATE
|
|
71
|
+
else:
|
|
72
|
+
if mesh_mode:
|
|
73
|
+
self.count_retransmission()
|
|
74
|
+
|
|
75
|
+
def set_metadata(self, metadata, hop, mesh_mode):
|
|
76
|
+
if metadata:
|
|
77
|
+
new_file = CTP_File(name = metadata[1], length = metadata[0])
|
|
78
|
+
self.set_current_file(new_file)
|
|
79
|
+
if mesh_mode:
|
|
80
|
+
self.reset_retransmission_counter(hop)
|
|
81
|
+
|
|
82
|
+
self.state = Digital_Endpoint.PROCESS_CHUNK_STATE
|
|
83
|
+
else:
|
|
84
|
+
if mesh_mode:
|
|
85
|
+
self.count_retransmission()
|
|
86
|
+
|
|
87
|
+
def get_next_chunk(self):
|
|
88
|
+
try:
|
|
89
|
+
self.current_chunk = self.current_file.get_missing_chunks()[0]
|
|
90
|
+
return self.current_chunk
|
|
91
|
+
except:
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
def set_data(self, data, hop, mesh_mode):
|
|
95
|
+
if data:
|
|
96
|
+
self.current_file.add_chunk(self.current_chunk, data)
|
|
97
|
+
if mesh_mode:
|
|
98
|
+
self.reset_retransmission_counter(hop)
|
|
99
|
+
else:
|
|
100
|
+
if mesh_mode:
|
|
101
|
+
self.count_retransmission()
|
|
102
|
+
|
|
103
|
+
if len(self.current_file.get_missing_chunks()) <= 0:
|
|
104
|
+
self.state = Digital_Endpoint.OK
|
|
105
|
+
return self.current_file
|