33from enum import Enum
44import glob
55import logging
6+ import time
67from typing import Union
78from defs .common import strtoint
89import itertools
1112import os
1213import ast
1314
15+ from typing import TYPE_CHECKING
16+
17+ if TYPE_CHECKING :
18+ from configparser import SectionProxy
19+
1420class Data_Type (Enum ):
1521 BYTE = 1
1622 '''8bit byte'''
@@ -206,6 +212,12 @@ class registry_map_entry:
206212 read_command : bytes = None
207213 ''' for transports/protocols that require sending a command ontop of "register" '''
208214
215+ read_interval : int = 1000
216+ ''' how often to read register in ms'''
217+
218+ next_read_timestamp : int = 0
219+ ''' unix timestamp in ms '''
220+
209221 write_mode : WriteMode = WriteMode .READ
210222 ''' enable disable reading/writing '''
211223
@@ -240,12 +252,14 @@ class protocol_settings:
240252 settings : dict [str , str ]
241253 ''' default settings provided by protocol json '''
242254
255+ transport_settings : 'SectionProxy' = None
256+
243257 byteorder : str = "big"
244258
245259 _log : logging .Logger = None
246260
247261
248- def __init__ (self , protocol : str , settings_dir : str = 'protocols' ):
262+ def __init__ (self , protocol : str , transport_settings : 'SectionProxy' = None , settings_dir : str = 'protocols' ):
249263
250264 #apply log level to logger
251265 self ._log_level = getattr (logging , logging .getLevelName (logging .getLogger ().getEffectiveLevel ()), logging .INFO )
@@ -254,6 +268,7 @@ def __init__(self, protocol : str, settings_dir : str = 'protocols'):
254268
255269 self .protocol = protocol
256270 self .settings_dir = settings_dir
271+ self .transport_settings = transport_settings
257272
258273 #load variable mask
259274 self .variable_mask = []
@@ -358,13 +373,22 @@ def load__registry(self, path, registry_type : Registry_Type = Registry_Type.INP
358373 registry_map : list [registry_map_entry ] = []
359374 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}))' )
360375
376+ read_interval_regex = re .compile (r'(?P<value>[\.\d]+)(?P<unit>[xs]|ms)' )
377+
378+
361379 data_type_regex = re .compile (r'(?P<datatype>\w+)\.(?P<length>\d+)' )
362380
363381 range_regex = re .compile (r'(?P<reverse>r|)(?P<start>(?:0?x[\da-z]+|[\d]+))[\-~](?P<end>(?:0?x[\da-z]+|[\d]+))' )
364382 ascii_value_regex = re .compile (r'(?P<regex>^\[.+\]$)' )
365383 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*(?:,|$)' )
366384
367385
386+ #load read_interval from transport settings, for #x per register read intervals
387+ transport_read_interval : int = 1000
388+ if self .transport_settings is not None :
389+ transport_read_interval = self .transport_settings .getint ("read_interval" , transport_read_interval )
390+
391+
368392 if not os .path .exists (path ): #return empty is file doesnt exist.
369393 return registry_map
370394
@@ -392,10 +416,38 @@ def process_row(row):
392416 # Initialize variables to hold numeric and character parts
393417 unit_multiplier : float = 1
394418 unit_symbol : str = ''
419+ read_interval : int = 0
420+ ''' read interval in ms '''
395421
396422 #clean up doc name, for extra parsing
397423 row ['documented name' ] = row ['documented name' ].strip ().lower ().replace (' ' , '_' )
398424
425+ #region read_interval
426+
427+
428+ if 'read interval' in row :
429+ row ['read interval' ] = row ['read interval' ].lower () #ensure is all lower case
430+ match = read_interval_regex .search (row ['read interval' ])
431+ if match :
432+ unit = match .group ('unit' )
433+ value = match .group ('value' )
434+ if value :
435+ value = float (value )
436+ if unit == 'x' :
437+ read_interval = int ((transport_read_interval * 1000 ) * value )
438+ else : # seconds or ms
439+ read_interval = value
440+ if unit != 'ms' :
441+ read_interval *= 1000
442+
443+ if read_interval == 0 :
444+ read_interval = transport_read_interval * 1000
445+ if "read_interval" in self .settings :
446+ try :
447+ read_interval = int (self .settings ['read_interval' ])
448+ except ValueError :
449+ read_interval = transport_read_interval * 1000
450+
399451
400452 #region overrides
401453 if overrides is not None :
@@ -589,6 +641,9 @@ def process_row(row):
589641 if "writable" in row :
590642 writeMode = WriteMode .fromString (row ['writable' ])
591643
644+ if "write" in row :
645+ writeMode = WriteMode .fromString (row ['write' ])
646+
592647 for i in r :
593648 item = registry_map_entry (
594649 registry_type = registry_type ,
@@ -608,6 +663,7 @@ def process_row(row):
608663 value_max = value_max ,
609664 value_regex = value_regex ,
610665 read_command = read_command ,
666+ read_interval = read_interval ,
611667 write_mode = writeMode
612668 )
613669 registry_map .append (item )
@@ -706,7 +762,8 @@ def process_row(row):
706762
707763 return registry_map
708764
709- def calculate_registry_ranges (self , map : list [registry_map_entry ], max_register : int ) -> list [tuple ]:
765+ def calculate_registry_ranges (self , map : list [registry_map_entry ], max_register : int , init : bool = False ) -> list [tuple ]:
766+
710767 ''' read optimization; calculate which ranges to read'''
711768 max_batch_size = 45 #see manual; says max batch is 45
712769
@@ -719,6 +776,10 @@ def calculate_registry_ranges(self, map : list[registry_map_entry], max_register
719776 start = - max_batch_size
720777 ranges : list [tuple ] = []
721778
779+ timestamp_ms = int (time .time () * 1000 )
780+ if init : #hack so that all registers are read initially without adding extra if in loop
781+ timestamp_ms = 0
782+
722783 while (start := start + max_batch_size ) <= max_register :
723784
724785 registers : list [int ] = [] #use a list, im too lazy to write logic
@@ -731,7 +792,10 @@ def calculate_registry_ranges(self, map : list[registry_map_entry], max_register
731792 if register .write_mode == WriteMode .WRITEONLY : ##Write Only; skip
732793 continue
733794
734- registers .append (register .register )
795+ #we are assuming calc registry ranges is being called EVERY READ.
796+ if register .next_read_timestamp < timestamp_ms :
797+ register .next_read_timestamp = timestamp_ms + register .read_interval
798+ registers .append (register .register )
735799
736800 if registers : #not empty
737801 ranges .append ((min (registers ), max (registers )- min (registers )+ 1 )) ## APPENDING A TUPLE!
@@ -781,7 +845,7 @@ def load_registry_map(self, registry_type : Registry_Type, file : str = '', sett
781845 size = item .register
782846
783847 self .registry_map_size [registry_type ] = size
784- self .registry_map_ranges [registry_type ] = self .calculate_registry_ranges (self .registry_map [registry_type ], self .registry_map_size [registry_type ])
848+ self .registry_map_ranges [registry_type ] = self .calculate_registry_ranges (self .registry_map [registry_type ], self .registry_map_size [registry_type ], init = True )
785849
786850 def process_register_bytes (self , registry : dict [int ,bytes ], entry : registry_map_entry ):
787851 ''' process bytes into data'''
0 commit comments