Skip to content

Commit d447c32

Browse files
committed
debug and finish canbus ( working now )
1 parent 868a40f commit d447c32

File tree

5 files changed

+88
-32
lines changed

5 files changed

+88
-32
lines changed

classes/protocol_settings.py

+33-11
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,8 @@ class protocol_settings:
233233
settings : dict[str, str]
234234
''' default settings provided by protocol json '''
235235

236+
byteorder : str = "big"
237+
236238
def __init__(self, protocol : str, settings_dir : str = 'protocols'):
237239
self.protocol = protocol
238240
self.settings_dir = settings_dir
@@ -266,6 +268,9 @@ def __init__(self, protocol : str, settings_dir : str = 'protocols'):
266268
else:
267269
self.transport = "modbus_rtu"
268270

271+
if "byteorder" in self.settings: #handle byte order for ints n stuff
272+
self.byteorder = self.settings["byteorder"]
273+
269274

270275
for registry_type in Registry_Type:
271276
self.load_registry_map(registry_type)
@@ -321,13 +326,13 @@ def load__json(self, file : str = '', settings_dir : str = ''):
321326

322327
def load__registry(self, path, registry_type : Registry_Type = Registry_Type.INPUT) -> list[registry_map_entry]:
323328
registry_map : list[registry_map_entry] = []
324-
register_regex = re.compile(r'(?P<register>(?:0?x[\dA-Z]+|[\d]+))\.(b(?P<bit>x?\d{1,2})|(?P<byte>x?\d{1,2}))')
329+
register_regex = re.compile(r'(?P<register>(?:0?x[\da-z]+|[\d]+))\.(b(?P<bit>x?\d{1,2})|(?P<byte>x?\d{1,2}))')
325330

326331
data_type_regex = re.compile(r'(?P<datatype>\w+)\.(?P<length>\d+)')
327332

328-
range_regex = re.compile(r'(?P<reverse>r|)(?P<start>(?:0?x[\dA-Z]+|[\d]+))[\-~](?P<end>(?:0?x[\dA-Z]+|[\d]+))')
333+
range_regex = re.compile(r'(?P<reverse>r|)(?P<start>(?:0?x[\da-z]+|[\d]+))[\-~](?P<end>(?:0?x[\da-z]+|[\d]+))')
329334
ascii_value_regex = re.compile(r'(?P<regex>^\[.+\]$)')
330-
list_regex = re.compile(r'\s*(?:(?P<range_start>(?:0?x[\dA-Z]+|[\d]+))-(?P<range_end>(?:0?x[\dA-Z]+|[\d]+))|(?P<element>[^,\s][^,]*?))\s*(?:,|$)')
335+
list_regex = re.compile(r'\s*(?:(?P<range_start>(?:0?x[\da-z]+|[\d]+))-(?P<range_end>(?:0?x[\da-z]+|[\d]+))|(?P<element>[^,\s][^,]*?))\s*(?:,|$)')
331336

332337

333338
if not os.path.exists(path): #return empty is file doesnt exist.
@@ -471,6 +476,7 @@ def determine_delimiter(first_row) -> str:
471476
register : int = -1
472477
register_bit : int = 0
473478
register_byte : int = -1
479+
row['register'] = row['register'].lower() #ensure is all lower case
474480
match = register_regex.search(row['register'])
475481
if match:
476482
register = strtoint(match.group('register'))
@@ -685,13 +691,13 @@ def process_register_bytes(self, registry : dict[int,bytes], entry : registry_ma
685691
register = register[:entry.data_type_size]
686692

687693
if entry.data_type == Data_Type.UINT:
688-
value = int.from_bytes(register[:4], byteorder='big', signed=False)
694+
value = int.from_bytes(register[:4], byteorder=self.byteorder, signed=False)
689695
elif entry.data_type == Data_Type.INT:
690-
value = int.from_bytes(register[:4], byteorder='big', signed=True)
696+
value = int.from_bytes(register[:4], byteorder=self.byteorder, signed=True)
691697
elif entry.data_type == Data_Type.USHORT:
692-
value = int.from_bytes(register[:2], byteorder='big', signed=False)
698+
value = int.from_bytes(register[:2], byteorder=self.byteorder, signed=False)
693699
elif entry.data_type == Data_Type.SHORT:
694-
value = int.from_bytes(register[:2], byteorder='big', signed=True)
700+
value = int.from_bytes(register[:2], byteorder=self.byteorder, signed=True)
695701
elif entry.data_type == Data_Type._16BIT_FLAGS or entry.data_type == Data_Type._8BIT_FLAGS or entry.data_type == Data_Type._32BIT_FLAGS:
696702
#16 bit flags
697703
start_bit : int = 0
@@ -704,7 +710,7 @@ def process_register_bytes(self, registry : dict[int,bytes], entry : registry_ma
704710
#handle custom sizes, less than 1 register
705711
end_bit = flag_size + start_bit
706712

707-
if entry.documented_name+'_codes' in self.protocolSettings.codes:
713+
if entry.documented_name+'_codes' in self.codes:
708714
flags : list[str] = []
709715
for i in range(start_bit, end_bit): # Iterate over each bit position (0 to 15)
710716
byte = i // 8
@@ -713,8 +719,8 @@ def process_register_bytes(self, registry : dict[int,bytes], entry : registry_ma
713719
# Check if the i-th bit is set
714720
if (val >> bit) & 1:
715721
flag_index = "b"+str(i)
716-
if flag_index in self.protocolSettings.codes[entry.documented_name+'_codes']:
717-
flags.append(self.protocolSettings.codes[entry.documented_name+'_codes'][flag_index])
722+
if flag_index in self.codes[entry.documented_name+'_codes']:
723+
flags.append(self.codes[entry.documented_name+'_codes'][flag_index])
718724

719725
value = ",".join(flags)
720726
else:
@@ -766,6 +772,22 @@ def process_register_bytes(self, registry : dict[int,bytes], entry : registry_ma
766772
except UnicodeDecodeError as e:
767773
print("UnicodeDecodeError:", e)
768774

775+
#apply unit mod
776+
if entry.unit_mod != float(1):
777+
value = value * entry.unit_mod
778+
779+
#apply codes
780+
if (entry.data_type != Data_Type._16BIT_FLAGS and
781+
entry.documented_name+'_codes' in self.codes):
782+
try:
783+
cleanval = str(int(value))
784+
785+
if cleanval in self.codes[entry.documented_name+'_codes']:
786+
value = self.codes[entry.documented_name+'_codes'][cleanval]
787+
except:
788+
#do nothing; try is for intval
789+
value = value
790+
769791
return value
770792

771793

@@ -859,7 +881,7 @@ def process_register_ushort(self, registry : dict[int, int], entry : registry_ma
859881
bit_index = entry.register_bit
860882
value = (registry[entry.register] >> bit_index) & bit_mask
861883
elif entry.data_type == Data_Type.ASCII:
862-
value = registry[entry.register].to_bytes((16 + 7) // 8, byteorder='big') #convert to ushort to bytes
884+
value = registry[entry.register].to_bytes((16 + 7) // 8, byteorder=self.byteorder) #convert to ushort to bytes
863885
try:
864886
value = value.decode("utf-8") #convert bytes to ascii
865887
except UnicodeDecodeError as e:

classes/transports/canbus.py

+38-17
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import time
33
import can
44
import asyncio
5+
import threading
6+
57

68

79
from .transport_base import transport_base
@@ -27,7 +29,13 @@ class canbus(transport_base):
2729
bus : can.BusABC = None
2830
''' holds canbus interface'''
2931

30-
lock : asyncio.Lock = asyncio.Lock()
32+
reader = can.AsyncBufferedReader()
33+
34+
35+
thread : threading.Thread
36+
''' main thread for async loop'''
37+
38+
lock : threading.Lock = threading.Lock()
3139
loop : asyncio.AbstractEventLoop = None
3240

3341
cache : dict [int,(bytes, float)] = {}
@@ -53,23 +61,38 @@ def __init__(self, settings : 'SectionProxy', protocolSettings : 'protocol_setti
5361
self.cacheTimeout = settings.getint(["cacheTimeout", "cache_timeout"], self.cacheTimeout)
5462

5563
self.bus = can.interface.Bus(interface=self.interface, channel=self.port, bitrate=self.baudrate)
64+
self.reader = can.AsyncBufferedReader()
65+
5666

5767
# Set up an event loop and run the async function
5868
self.loop = asyncio.get_event_loop()
59-
self.loop.run_until_complete(self.read_bus(self.bus))
69+
70+
notifier = can.Notifier(self.bus, [self.reader], loop=self.loop)
71+
72+
73+
thread = threading.Thread(target=self.start_loop)
74+
thread.start()
75+
76+
self.connected = True
6077

6178
self.init_after_connect()
62-
79+
80+
def start_loop(self):
81+
self.loop.run_until_complete(self.read_bus(self.bus))
6382

6483
async def read_bus(self, bus : can.BusABC):
6584
''' read canbus asynco and store results in cache'''
6685
while True:
67-
msg : can.Message = await bus.recv() # This will be non-blocking with asyncio
86+
msg = await self.reader.get_message() # This will be non-blocking with asyncio
6887
if msg:
6988
print(f"Received message: {msg.arbitration_id:X}, data: {msg.data}")
7089

71-
async with self.lock:
72-
self.cache[msg.arbitration_id] = (msg.data, time.time())
90+
with self.lock:
91+
#convert bytearray to bytes; were working with bytes.
92+
self.cache[msg.arbitration_id] = (bytes(msg.data), time.time())
93+
94+
await asyncio.sleep(0.5)
95+
7396

7497
def clean_cache(self):
7598
current_time = time.time()
@@ -78,9 +101,9 @@ def clean_cache(self):
78101
# Create a list of keys to remove (don't remove while iterating)
79102
keys_to_delete = [msg_id for msg_id, (_, timestamp) in self.cache.items() if current_time - timestamp > self.cacheTimeout]
80103

81-
# Remove old messages from the dictionary
82-
for key in keys_to_delete:
83-
del self.cache[key]
104+
# Remove old messages from the dictionary
105+
for key in keys_to_delete:
106+
del self.cache[key]
84107

85108
def init_after_connect(self):
86109
return True
@@ -142,22 +165,20 @@ def write_data(self, data : dict[str, str]) -> None:
142165
def read_data(self) -> dict[str, str]:
143166
''' because canbus is passive / broadcast, were just going to read from the cache '''
144167

145-
self.clean_cache() #clean cache of old data
146-
147168
info = {}
148-
#modbus - only read input/holding registries
149-
for registry_type in (Registry_Type.ZERO):
150169

151-
#remove timestamp for processing
152-
registry = {key: value[0] for key, value in self.cache.items()}
170+
#remove timestamp for processing
171+
registry = {key: value[0] for key, value in self.cache.items()}
153172

154-
new_info = self.protocolSettings.process_registery(registry, self.protocolSettings.get_registry_map(registry_type))
173+
new_info = self.protocolSettings.process_registery(registry, self.protocolSettings.get_registry_map(Registry_Type.ZERO))
155174

156-
info.update(new_info)
175+
info.update(new_info)
157176

158177
if not info:
159178
self._log.info("Register/Cache is Empty; no new information reported.")
160179

180+
self.clean_cache() #clean cache of old data
181+
161182
return info
162183

163184
def read_variable(self, variable_name : str, registry_type : Registry_Type, entry : registry_map_entry = None):

classes/transports/transport_base.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def _get_top_class_name(cls, cls_obj):
7979
else:
8080
return cls._get_top_class_name(cls_obj.__bases__[0])
8181

82-
def connect(self, transports : 'transport_base'):
82+
def connect(self):
8383
pass
8484

8585
def write_data(self, data : dict[str, registry_map_entry]):

defs/common.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,24 @@ def strtoint(val : str) -> int:
1919

2020
if isinstance(val, int): #is already int.
2121
return val
22+
23+
val = val.lower()
2224

2325
if val and val[0] == 'x':
24-
return int.from_bytes(bytes.fromhex(val[1:]), byteorder='big')
26+
val = val[1:]
27+
# Pad the string with a leading zero
28+
if len(val) % 2 != 0:
29+
val = '0' + val
30+
31+
return int.from_bytes(bytes.fromhex(val), byteorder='big')
2532

2633
if val and val.startswith("0x"):
27-
return int.from_bytes(bytes.fromhex(val[2:]), byteorder='big')
34+
val = val[2:]
35+
# Pad the string with a leading zero
36+
if len(val) % 2 != 0:
37+
val = '0' + val
38+
39+
return int.from_bytes(bytes.fromhex(val), byteorder='big')
2840

2941
if not val: #empty
3042
return 0
+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
22
"transport" : "canbus",
3-
"baud" : 500000
3+
"baud" : 500000,
4+
"byteorder" : "little"
45
}

0 commit comments

Comments
 (0)