def __init__(self, directory, proto_obj, send_retries, msg_size_max, msg_send_limit=5): if msg_size_max > proto_obj.Mtu: raise MessageExchangeExceptionMessageSize(msg_size_max, proto_obj.Mtu) # Initialize the MessageExchangeService class. super().__init__() # Configure the max size of a single message. Determines buffer sizes. MessageTemplate.Configure(msg_size_max) # Configure the MessageBuffer and Message classes. MessageBuffer.Configure(directory) # Create a generic send buffer for all outgoing messages. self.SendMessageBuffer = MessageBuffer( 'send', -1, -1, MessageExchange.SEND_BUFFER_SIZE) self.MessageMappings = set() self.Protocol = proto_obj self.SendRetries = send_retries self.SendLimit = msg_send_limit self.ConnectionState = Subject() self.ConnectionState.State = False self.ServiceInit = False self.NewMessage = False # Set to True by the receive callback on a received message. self.Log = ExtLogging.Create("MsgEx") MessageExchange._Instance = self
class Message: Msg = None _StreamBuffer = None _Parser = None Log = ExtLogging.Create("Msg") @staticmethod def SetParser(parser_obj): Message._Parser = parser_obj Message.Log.debug("Parser set: {}".format(Message._Parser)) @staticmethod def Message(): return Message.Msg @staticmethod def Stream(): return Message._StreamBuffer @staticmethod def Serialize(data_dict, meta_dict=None): # Create a message from the template. Message.Msg = MessageTemplate() Message.Msg = Message.Msg.Msg Message.Log.debug( "Serializing message with metadata: {} | data: {}".format( meta_dict, data_dict)) # Close the previous stream if there is one. if Message._StreamBuffer is not None: Message._StreamBuffer.close() Message._StreamBuffer = uio.BytesIO(MessageTemplate.MsgSizeMax) # If metadata was given, check for matching keys and copy the values. if meta_dict is not None: for key in Message.Msg[MessageTemplate.MSG_SECTION_META].keys(): if key in meta_dict.keys(): Message.Msg[ MessageTemplate.MSG_SECTION_META][key] = meta_dict[key] # Copy the data dictionary. Message.Msg[MessageTemplate.MSG_SECTION_DATA] = data_dict # Serialize the message using the parser. Message._Parser.Dumps(Message.Msg, Message._StreamBuffer) return Message._StreamBuffer @staticmethod def Deserialize(msg_str): try: Message.Msg = Message._Parser.Loads(msg_str) except ValueError: Message.Log.error("Invalid format: {}".format(msg_str)) return Message.Msg @staticmethod def Print(): Message.Log.info(Message.Msg)
def __init__(self, msg_ex_obj, reg_info_spec): """ Registration object, implements the RegistrationService and Observer classes. :param msg_ex_obj: MessageExchange object :type msg_ex_obj: <MessageExchange> :param reg_info_spec: Registration info specification :type reg_info_spec: <<MessageSpecification>RegistrationInfo> """ super().__init__() self.MsgEx = msg_ex_obj self.RegInfoSpec = reg_info_spec self.Version = Version.Instance() self.Log = ExtLogging.Create("Reg") return
def test_LoggingToFileMaxLines(self): file = "extlog" name = "test" text = "hi" file_limit = 2 line_limit = 10 ExtLogging.ConfigGlobal(level=ExtLogging.INFO, stream=TestStream(), dir=self.DIR, file_prefix=file, line_limit=line_limit, file_limit=file_limit, print_enabled=True, timestamp_enabled=True) log = ExtLogging.Create(name) print(log) for f in range(0, file_limit): for l in range(0, line_limit): log.info(text + str(l)) ExtLogging.Mngr.PrintList() file_list = ExtLogging.Mngr.List() print(file_list) count = ExtLogging.Mngr.Count() self.assertEqual(count, file_limit) for i in range(0, count): print("[UT] Checking file: {}".format(file_list[i])) f = open(file_list[i], 'r') for l in range(0, line_limit): exc_occurred = False try: line = f.readline() print("[UT] Read line: {}".format(line)) except OSError: exc_occurred = True self.assertFalse(exc_occurred) self.assertTrue(text in line) self.assertTrue(name in line) self.assertTrue("[" in line) f.close()
def __init__(self, directory: str, name: str, filter_depth: int, sensor_driver_obj, samples_per_update: int = 1, dec_round: bool = True, store_data: bool = True): """ :param directory: :type directory: :param name: :type name: :param filter_depth: :type filter_depth: :param sensor_driver_obj: :type sensor_driver_obj: :param samples_per_update: :type samples_per_update: :param dec_round: :type dec_round: :param store_data: :type store_data: """ if samples_per_update > 1 and store_data is False: raise SensorExceptionInvalidConfig( "store_data must be True if samples_per_update > 1") # Initialize the SensorService class super().__init__(name) self.SensorDriver = sensor_driver_obj self.SamplesPerUpdate = samples_per_update self.Filter = AvgFilter.AvgFilter(filter_depth, dec_round) if store_data is True: self.SampleQueue = NvQueue.NvQueue(directory + '/' + name, Sensor.SAMPLE_FMT, Sensor.FILE_SAMPLES_MAX) self.NewSample = Subject() self.NewSample.State = list() self.StoreData = store_data self.Log = ExtLogging.Create("Sensor-{}".format(name))
from upyiot.system.Service.Service import Service from upyiot.system.Service.Service import ServiceExceptionSuspend from upyiot.system.Service.Service import ServiceException from upyiot.middleware.StructFile.StructFile import StructFile from upyiot.system.ExtLogging import ExtLogging import utime from micropython import const Log = ExtLogging.Create("Scheduler") class SchedulerException(Exception): def __init__(self): return class SchedulerExceptionStopped(SchedulerException): def __init__(self): return class SchedulerExceptionDeepSleepFailed(SchedulerException): def __init__(self): return class SchedulerMemory: FILE_SCHEDULER_MEMORY = "/sched_mem" SVC_NAME_LEN = const(20)
from upyiot.middleware.NvQueue import NvQueue from upyiot.system.ExtLogging import ExtLogging from upyiot.comm.Messaging.MessageTemplate import MessageTemplate Log = ExtLogging.Create("MsgBuf") class MessageBuffer: MSG_STRUCT_TYPE = const(0) MSG_STRUCT_SUBTYPE = const(1) MSG_STRUCT_LEN = const(2) MSG_STRUCT_DATA = const(3) MsgDataLen = 0 MsgStructFmt = "" Directory = "" _UnPackBuffer = None Configured = False @staticmethod def Configure(directory): MessageBuffer.MsgDataLen = MessageTemplate.MsgSizeMax MessageBuffer.MsgStructFmt = "<iiI" + \ str(MessageBuffer.MsgDataLen) + "s" Log.debug("FMT: {}".format(MessageBuffer.MsgStructFmt)) MessageBuffer.Directory = directory + "/" Log.debug(directory) # MessageBuffer._UnPackBuffer = bytearray(msg_len_max + msg_len_max) MessageBuffer.Configured = True
def __init__( self, spi, pins, ttn_config, channel=0, # compatibility with Dragino LG02, set to None otherwise fport=1, lora_parameters=_default_parameters): self._spi = spi self._pins = pins self._parameters = lora_parameters self._lock = False self._log = ExtLogging.Create("SX127x") # setting pins if "dio_0" in self._pins: self._pin_rx_done = Pin(self._pins["dio_0"], Pin.IN) self._irq = Pin(self._pins["dio_0"], Pin.IN) if "ss" in self._pins: self._pin_ss = Pin(self._pins["ss"], Pin.OUT) if "led" in self._pins: self._led_status = Pin(self._pins["led"], Pin.OUT) # check hardware version init_try = True re_try = 0 while init_try and re_try < 5: version = self.read_register(REG_VERSION) re_try = re_try + 1 self._log.info("SX version: {}".format(version)) if version == 0x12: init_try = False else: utime.sleep_ms(1000) if version != 0x12: raise Exception('Invalid version.') # Set frequency registers self._rfm_msb = None self._rfm_mid = None self._rfm_lsb = None # init framecounter self.frame_counter = 0 self._fport = fport # Set datarate registers self._sf = None self._bw = None self._modemcfg = None # ttn configuration if "US" in ttn_config.country: from ttn.ttn_usa import TTN_FREQS self._frequencies = TTN_FREQS elif ttn_config.country == "AS": from ttn.ttn_as import TTN_FREQS self._frequencies = TTN_FREQS elif ttn_config.country == "AU": from ttn.ttn_au import TTN_FREQS self._frequencies = TTN_FREQS elif ttn_config.country == "EU": from ttn.ttn_eu import TTN_FREQS self._frequencies = TTN_FREQS else: raise TypeError("Country Code Incorrect/Unsupported") # Give the uLoRa object ttn configuration self._ttn_config = ttn_config # put in LoRa and sleep mode self.sleep() # set channel number self._channel = channel self._actual_channel = channel if self._channel is not None: self.set_frequency(self._channel) # set data rate and bandwidth self.set_bandwidth(self._parameters["signal_bandwidth"]) # set LNA boost self.write_register(REG_LNA, self.read_register(REG_LNA) | 0x03) # set auto AGC self.write_register(REG_MODEM_CONFIG, 0x04) self.implicit_header_mode(self._parameters['implicit_header']) self.set_tx_power(self._parameters['tx_power_level']) self.set_coding_rate(self._parameters['coding_rate']) self.set_sync_word(self._parameters['sync_word']) self.enable_CRC(self._parameters['enable_CRC']) #self.invert_IQ(self._parameters["invert_IQ"]) self.set_preamble_length(self._parameters['preamble_length']) self.set_spreading_factor(self._parameters['spreading_factor']) # set LowDataRateOptimize flag if symbol time > 16ms (default disable on reset) # self.write_register(REG_MODEM_CONFIG, self.read_register(REG_MODEM_CONFIG) & 0xF7) # default disable on reset #bw_parameter = self._parameters["signal_bandwidth"] #sf_parameter = self._parameters["spreading_factor"] #if 1000 / (bw_parameter / 2**sf_parameter) > 16: # self.write_register( # REG_MODEM_CONFIG, # self.read_register(REG_MODEM_CONFIG) | 0x08 # ) # set base addresses self.write_register(REG_FIFO_TX_BASE_ADDR, FifoTxBaseAddr) self.write_register(REG_FIFO_RX_BASE_ADDR, FifoRxBaseAddr) self.standby()
from upyiot.comm.Messaging.Protocol.MessagingProtocol import MessagingProtocol from upyiot.comm.Messaging.MessageExchange import MessageExchange from upyiot.system.ExtLogging import ExtLogging from micropython import const import urequests Log = ExtLogging.Create("HttpProto") class HttpProtocol(MessagingProtocol): HTTP_MTU = const(1400) _Instance = None def __init__(self): super().__init__(None, self.HTTP_MTU) HttpProtocol._Instance = self return def Setup(self, recv_callback, msg_mappings): MessagingProtocol.Setup(self, recv_callback, msg_mappings) return def Send(self, msg_map, payload, size): route = msg_map[MessageExchange.MSG_MAP_ROUTING] decoded = payload.decode("utf-8") print(type(payload), type(decoded)) Log.info("POST to {}: {}".format(route, decoded)) headers = { 'Content-type': 'application/json',
from upyiot.comm.Messaging.Protocol.MessagingProtocol import MessagingProtocol from upyiot.comm.Messaging.MessageExchange import MessageExchange from upyiot.comm.Network import LoRaWAN from upyiot.comm.Network.LoRaWAN.MHDR import MHDR from upyiot.middleware.StructFile import StructFile from upyiot.drivers.Modems.SX127x.LoRa import * from upyiot.drivers.Modems.SX127x.board_config import BOARD from upyiot.system.ExtLogging import ExtLogging from time import sleep from random import randrange import machine from micropython import const Log = ExtLogging.Create("LoraProto") ADDR_SIZE = const(4) EUI_SIZE = const(8) KEY_SIZE = const(16) TX_TIMEOUT_SEC = const(10) RX_TIMEOUT_SEC = const(30) TIMEOUT_POLL_SEC = const(1) class LoRaWANParams: SESSION_DATA_FMT = "<4s16s16s" # DevAddr, AppSKey, NwkSKey FCNT_DATA_FMT = "<I" def __init__(self, dir: str, dev_eui: str):
from upyiot.middleware.StructFile import StructFile from micropython import const from upyiot.system.ExtLogging import ExtLogging Log = ExtLogging.Create("NvQueue") class NvQueueIterator(object): def __init__(self, nvqueue_obj): print(nvqueue_obj) nvqueue_obj.Queue.IteratorConfig(nvqueue_obj.ReadOffset, nvqueue_obj.WriteOffset, nvqueue_obj.Count) self.Iterator = iter(nvqueue_obj.Queue) def __next__(self): return self.Iterator.__next__() class NvQueue: # Capacity, count, read offset, write offset. META_FMT = "<HHHH" META_STRUCT_CAP = const(0) META_STRUCT_CNT = const(1) META_STRUCT_R_OFF = const(2) META_STRUCT_W_OFF = const(3) def __init__(self, file, data_fmt, capacity): Log.info( "Creating NvQueue with path: {}, capacity: {}, format: {}".format(
from micropython import const from upyiot.middleware.SubjectObserver.SubjectObserver import Observer from upyiot.system.ExtLogging import ExtLogging Log = ExtLogging.Create("MsgFmt") class MessagePartSource: def __init__(self, msg_formatter_obj, key, complete_count): self.MsgFormatter = msg_formatter_obj self.Key = key self.Count = 0 self.CompleteCount = complete_count def Put(self, data): if self.Count is 0: self.MsgFormatter.MessagePartAdd(self.Key, data) else: self.MsgFormatter.MessagePartAppend(self.Key, data) self._CountInc() def _CountInc(self): self.Count += 1 if self.Count is self.CompleteCount: self.Count = 0 self.MsgFormatter.MessagePartFinalize() class MessagePartSink: def __init__(self, msg_formatter_obj, key, complete_count): self.MsgFormatter = msg_formatter_obj
def Setup(self): for dir in self.DIR_TREE.values(): try: uos.mkdir(dir) except OSError: print("Cannot create directory '{}'".format(dir)) # Configure the ExtLogging class. ExtLogging.ConfigGlobal(level=ExtLogging.DEBUG, stream=None, dir=self.DIR_TREE[self.DIR_LOG], file_prefix="log_", line_limit=1000, file_limit=10) StructFile.SetLogger(ExtLogging.Create("SFile")) self.Log = ExtLogging.Create("Main") self.Log.info("Device ID: {}".format(DeviceId.DeviceId())) Version(self.DIR_TREE[self.DIR_SYS], self.VER_MAJOR, self.VER_MINOR, self.VER_PATCH) rst_reason = ResetReason.ResetReason() self.Log.debug("Reset reason: {}".format( ResetReason.ResetReasonToString(rst_reason))) # Create driver instances. self.DummySensorDriver = DummySensor(self.DummySamples) self.InternalTemp = InternalTemp() # TODO: Enable actual sensor drivers. # self.TempSensorDriver = Mcp9700Temp(temp_pin_nr=Pins.CFG_HW_PIN_TEMP, # en_supply_obj=Supply(Pins.CFG_HW_PIN_TEMP_EN, 3.3, 300)) # # self.VBatSensorDriver = VoltageSensor(pin_nr=Pins.CFG_HW_PIN_VBAT_LVL, # en_supply_obj=Supply(Pins.CFG_HW_PIN_VBAT_LVL_EN, 3.3, 300)) if self.NETWORK is self.KPN: self.LoraProtocol = LoraProtocol( self.KpnLoraConfig, directory=self.DIR_TREE[self.DIR_LORA]) elif self.NETWORK is self.TTN: self.LoraProtocol = LoraProtocol( self.TtnLoraConfig, directory=self.DIR_TREE[self.DIR_LORA]) if self.NETWORK_REG is self.ABP: self.LoraProtocol.Params.StoreSession(self.DevAddr, self.AppSKey, self.NwkSKey) else: raise Exception("No valid network LoRa selected.") self.DummySensor = Sensor.Sensor(self.DIR_TREE[self.DIR_SENSOR], "Dummy", self.FILTER_DEPTH, self.DummySensorDriver, samples_per_update=3, dec_round=True, store_data=True) self.TempSensor = Sensor.Sensor( self.DIR_TREE[self.DIR_SENSOR], "Temp", self.FILTER_DEPTH, self. InternalTemp, # TODO: Replace InternalTemp driver with TempSensorDriver samples_per_update=2, dec_round=True, store_data=True) # self.BatteryVoltageSensor = Sensor.Sensor(self.DIR_TREE[self.DIR_SENSOR], # "BatLvl", # self.FILTER_DEPTH, # self.VBatSensorDriver, # samples_per_update=2, # dec_round=False, # store_data=True) self.MsgEx = MessageExchange(directory=self.DIR_TREE[self.DIR_MSG], proto_obj=self.LoraProtocol, send_retries=self.RETRIES, msg_size_max=self.LoraProtocol.Mtu, msg_send_limit=self.SEND_LIMIT) # Create the registration info spec and Registration service. # Link the Registration service to the Message Exchange service. The Message Exchange # service will activate the Registration service when it connects to the LoRa network. self.RegistrationInfo = RegistrationInfo() self.Registration = Registration(self.MsgEx, self.RegistrationInfo) self.MsgEx.AttachConnectionStateObserver(self.Registration) self.Scheduler = ServiceScheduler( deepsleep_threshold_sec=self.DEEPSLEEP_THRESHOLD_SEC, # deep_sleep_obj=PowerManager.PowerManager(), directory=self.DIR_TREE[self.DIR_SYS]) # Set service dependencies. # There are no hard dependencies between the services. # Register all services to the scheduler. self.Scheduler.ServiceRegister(self.DummySensor) self.Scheduler.ServiceRegister(self.TempSensor) # self.Scheduler.ServiceRegister(self.BatteryVoltageSensor) self.Scheduler.ServiceRegister(self.MsgEx) self.Scheduler.ServiceRegister(self.Registration) Message.SetParser(CborParser()) MessageTemplate.SectionsSet(Metadata.MSG_SECTION_META, Metadata.MSG_SECTION_DATA) MessageTemplate.MetadataTemplateSet(Metadata.Metadata, Metadata.MetadataFuncs) # Create message specifications. self.MoistReport = MoistureSensorReport() self.BatteryReport = BatterySensorReport() self.TempReport = TemperatureSensorReport() # Create MessageFormatters and couple them with their message specs. moist_report_meta = { Metadata.MSG_META_TYPE: self.MoistReport.Type, Metadata.MSG_META_SUBTYPE: self.MoistReport.Subtype, } self.MoistFmt = MessageFormatter(self.MsgEx, MessageFormatter.SEND_ON_CHANGE, self.MoistReport, moist_report_meta) battery_report_meta = { Metadata.MSG_META_TYPE: self.BatteryReport.Type, Metadata.MSG_META_SUBTYPE: self.BatteryReport.Subtype, } self.BatteryFmt = MessageFormatter(self.MsgEx, MessageFormatter.SEND_ON_CHANGE, self.BatteryReport, battery_report_meta) temp_report_meta = { Metadata.MSG_META_TYPE: self.TempReport.Type, Metadata.MSG_META_SUBTYPE: self.TempReport.Subtype, } self.TempFmt = MessageFormatter(self.MsgEx, MessageFormatter.SEND_ON_CHANGE, self.TempReport, temp_report_meta) # Register message specs for exchange. self.MsgEx.RegisterMessageType(self.MoistReport) self.MsgEx.RegisterMessageType(self.BatteryReport) self.MsgEx.RegisterMessageType(self.TempReport) self.MsgEx.RegisterMessageType(self.RegistrationInfo) # Create observers for the sensor data. self.MoistObserver = self.MoistFmt.CreateObserver( MoistureSensorReport.DATA_KEY_MEASUREMENTS) self.BatteryObserver = self.BatteryFmt.CreateObserver( BatterySensorReport.DATA_KEY_MEASUREMENTS) self.TempObserver = self.TempFmt.CreateObserver( TemperatureSensorReport.DATA_KEY_MEASUREMENTS) # Link the observers to the sensors. self.DummySensor.ObserverAttachNewSample(self.MoistObserver) self.TempSensor.ObserverAttachNewSample(self.TempObserver) self.Scheduler.RegisterCallbackBeforeDeepSleep(MainApp.BeforeSleep) # Set intervals for all services. self.MsgEx.SvcIntervalSet(self.MsgExInterval) self.MsgEx.DefaultIntervalSet(self.MsgExInterval) self.DummySensor.SvcIntervalSet(self.MoistReadInterval) self.TempSensor.SvcIntervalSet(self.SensorReadInterval) # Activate the Message Exchange to attempt to connect to the LoRa network # if no LoRaWAN session exists yet. if self.LoraProtocol.HasSession() is False: self.MsgEx.SvcActivate() # self.BatteryObserver.Update(100) self.Log.info("Finished initialization.")