class TestNodeParams: def setup_method(self, method): # Create NodeConfig instance self.nodeConfig = NodeConfig(configFilePath) if method.__name__ != "test_init": self.nodeParams = NodeParams( config=self.nodeConfig) # Create NodeParams instance def test_init(self): """Test NodeParams init function.""" # Test that init function loads node configuration from file print("Testing init with configFile") nodeParams = NodeParams(configFile=configFilePath) assert (nodeParams.config.__dict__ == self.nodeConfig.__dict__) # Test that init function loads node configuration from provided config nodeParams = NodeParams(config=self.nodeConfig) assert (nodeParams.config.__dict__ == self.nodeConfig.__dict__) def test_getCmdCounter(self): """Test NodeParams command counter get function.""" # Test getting random command counter counter = self.nodeParams.get_cmdCounter() assert (counter >= 1) # value within range assert (counter <= 65536) # value within range
class TestNodeCmdProcessor: def setup_method(self, method): self.nodeStatus = [NodeState(i + 1) for i in range(5)] self.nodeParams = NodeParams(configFile=configFilePath) msgParser = MsgParser( {'parseMsgMax': self.nodeParams.config.parseMsgMax}, SLIPMsg(256)) radio = Radio( [], { 'uartNumBytesToRead': self.nodeParams.config.uartNumBytesToRead, 'rxBufferSize': 2000 }) self.comm = SerialComm([NodeCmdProcessor], self.nodeParams, radio, msgParser) def test_processMsg(self): """Test processMsg method of NodeCmdProcessor.""" # Test processing of all NodeCmds for cmdId in cmdsToTest: cmdMsg = testCmds[cmdId].serialize() print(cmdId) assert (self.comm.processMsg(cmdMsg, args={ 'nodeStatus': self.nodeStatus, 'comm': self.comm, 'clock': self.nodeParams.clock }) == True) if cmdId == NodeCmds['NoOp']: pass elif cmdId == NodeCmds['ParamUpdate']: assert (self.nodeParams.config.parseMsgMax == unpack( '=H', testCmds[cmdId].cmdData['paramValue'])[0]) elif cmdId == NodeCmds['ConfigUpdate']: assert (self.nodeParams.newConfig != None ) # new config staged for updating cmdResponse = None for entry in self.nodeParams.cmdResponse: if (entry['cmdId'] == NodeCmds['ConfigUpdate']): cmdResponse = entry break assert (cmdResponse != None) assert (cmdResponse['cmdResponse'] == 1) def test_malformedCmd(self): # Test command with improper message contents badMsg = packHeader( createHeader([ CmdDict[NodeCmds['GCSCmd']].header, [NodeCmds['GCSCmd'], 0, self.nodeParams.get_cmdCounter()] ])) # header only assert (self.comm.processMsg(badMsg, args={ 'nodeStatus': self.nodeStatus, 'comm': self.comm, 'clock': self.nodeParams.clock }) == False)
class TestTDMACmdProcessor: def setup_method(self, method): self.nodeStatus = [NodeState(i + 1) for i in range(5)] self.nodeParams = NodeParams(configFile=configFilePath) msgParser = MsgParser( {'parseMsgMax': self.nodeParams.config.parseMsgMax}, SLIPMsg(256)) radio = Radio( [], { 'uartNumBytesToRead': self.nodeParams.config.uartNumBytesToRead, 'rxBufferSize': 2000 }) self.comm = TDMAComm([TDMACmdProcessor], radio, msgParser, self.nodeParams) def test_validateBlockTxRequest(self): """Test validateBlockTxRequest method of TDMACmdProcessor.""" # Test request rejected if start time passed contents = { 'startTime': time.time() - 1.0, 'length': self.nodeParams.config.commConfig['maxTxBlockSize'] } assert (validateBlockTxRequest(contents, [], self.nodeParams) == False) # Test request rejected if block too long contents = { 'startTime': time.time() + 1.0, 'length': self.nodeParams.config.commConfig['maxTxBlockSize'] + 1 } assert (validateBlockTxRequest(contents, [], self.nodeParams) == False) # Test for request acceptance contents = { 'startTime': time.time() + 1.0, 'length': self.nodeParams.config.commConfig['maxTxBlockSize'] } assert (validateBlockTxRequest(contents, [], self.nodeParams) == True) def test_processMsg(self): """Test processMsg method of TDMACmdProcessor.""" # Test processing of all TDMACmds for cmdId in cmdsToTest: self.comm.networkMsgQueue = [ ] # clear command queue to check for additions after command processing assert (self.comm.processMsg(testCmds[cmdId].serialize(), args={ 'nodeStatus': self.nodeStatus, 'comm': self.comm, 'clock': self.nodeParams.clock }) == True) if cmdId == TDMACmds['TimeOffset']: sourceId = testCmds[cmdId].header['header']['sourceId'] assert (self.nodeStatus[sourceId - 1].timeOffset == testCmds[cmdId].cmdData['nodeStatus'].timeOffset ) # verify time offset parsed correctly elif cmdId == TDMACmds['MeshStatus']: assert (self.comm.commStartTime == testCmds[cmdId].cmdData['commStartTimeSec'] ) # comm start time stored elif cmdId == TDMACmds['LinkStatus']: msgNodeId = testCmds[cmdId].cmdData['nodeId'] for i in range(0, self.nodeParams.config.maxNumNodes): assert (self.nodeParams.linkStatus[msgNodeId - 1][i] == testCmds[cmdId].cmdData['linkStatus'][msgNodeId - 1][i]) elif cmdId == TDMACmds['ConfigUpdate']: assert (self.nodeParams.newConfig != None ) # new config staged for updating assert (len(self.comm.networkMsgQueue) > 0) elif cmdId == TDMACmds['NetworkRestart']: assert (len(self.comm.networkMsgQueue) > 0) # Resend and test that commStartTime is not updated once it has previously been set cmdId = TDMACmds['MeshStatus'] self.comm.commStartTime = testCmds[cmdId].cmdData[ 'commStartTimeSec'] - 1 assert (self.comm.processMsg(testCmds[cmdId].serialize(), args={ 'nodeStatus': self.nodeStatus, 'comm': self.comm, 'clock': self.nodeParams.clock }) == True) assert (self.comm.commStartTime != testCmds[cmdId].cmdData['commStartTimeSec'] ) # comm start time should not have been updated def test_blockTxCmdsProcessing(self): """Test processing of block transmit related commands.""" return # skip this test self.comm.commStartTime = self.nodeParams.clock.getTime() - 1.0 blockReqID = random.randint(1, 255) # just a random "unique" number startTime = int(self.nodeParams.clock.getTime() + 10.0) length = self.nodeParams.config.commConfig['maxTxBlockSize'] txNode = 1 ## TDMACmds['BlockTxRequest'] cmdMsg = Command(TDMACmds['BlockTxRequest'], { 'blockReqID': blockReqID, 'startTime': startTime, 'length': length }, [ TDMACmds['BlockTxRequest'], txNode, self.nodeParams.get_cmdCounter() ]).serialize(self.nodeParams.clock.getTime()) # Process and check results assert (self.comm.processMsg(cmdMsg, args={ 'nodeStatus': self.nodeStatus, 'comm': self.comm, 'clock': self.nodeParams.clock }) == True) assert (len(self.comm.radio.txBuffer) == calcsize( CmdDict[TDMACmds['BlockTxRequestResponse']].packFormat) + calcsize(headers['NodeHeader']['format'])) # response sent assert (self.comm.blockTxStatus['blockReqID'] == blockReqID) assert (self.comm.blockTxStatus['status'] == TDMABlockTxStatus.pending) assert (self.comm.blockTxStatus['txNode'] == txNode) assert (self.comm.blockTxStatus['startTime'] == startTime) assert (self.comm.blockTxStatus['length'] == length) ## TDMACmds['BlockTxConfirmed'] time.sleep(0.01) cmdMsg = Command(TDMACmds['BlockTxConfirmed'], { 'blockReqID': blockReqID }, [ TDMACmds['BlockTxConfirmed'], txNode, self.nodeParams.get_cmdCounter() ]).serialize(self.nodeParams.clock.getTime()) assert (self.comm.processMsg(cmdMsg, args={ 'nodeStatus': self.nodeStatus, 'comm': self.comm, 'clock': self.nodeParams.clock }) == True) assert ( self.comm.blockTxStatus['status'] == TDMABlockTxStatus.confirmed ) # status updated to confirmed ## TDMACmds['BlockTxStatus'] self.comm.resetBlockTxStatus() time.sleep(0.01) cmdMsg = Command(TDMACmds['BlockTxStatus'], { 'blockReqID': blockReqID, 'startTime': startTime, 'length': length }, [ TDMACmds['BlockTxStatus'], txNode, self.nodeParams.get_cmdCounter() ]).serialize(self.nodeParams.clock.getTime()) # Check status updated assert (self.comm.processMsg(cmdMsg, args={ 'nodeStatus': self.nodeStatus, 'comm': self.comm, 'clock': self.nodeParams.clock }) == True) assert (len(self.comm.radio.txBuffer) == calcsize( CmdDict[TDMACmds['BlockTxRequestResponse']].packFormat) + calcsize(headers['NodeHeader']['format'])) # response sent assert (self.comm.blockTxStatus['blockReqID'] == blockReqID) assert ( self.comm.blockTxStatus['status'] == TDMABlockTxStatus.confirmed) assert (self.comm.blockTxStatus['txNode'] == txNode) assert (self.comm.blockTxStatus['startTime'] == startTime) assert (self.comm.blockTxStatus['length'] == length) # Check status updated to confirmed if only pending time.sleep(0.01) cmdMsg = Command(TDMACmds['BlockTxStatus'], { 'blockReqID': blockReqID, 'startTime': startTime, 'length': length }, [ TDMACmds['BlockTxStatus'], txNode, self.nodeParams.get_cmdCounter() ]).serialize(self.nodeParams.clock.getTime()) # update command counter self.comm.blockTxStatus['status'] = TDMABlockTxStatus.pending assert (self.comm.processMsg(cmdMsg, args={ 'nodeStatus': self.nodeStatus, 'comm': self.comm, 'clock': self.nodeParams.clock }) == True) assert ( self.comm.blockTxStatus['status'] == TDMABlockTxStatus.confirmed) ## TDMACmds['BlockTxRequestResponse'] time.sleep(0.01) self.comm.resetBlockTxStatus() self.comm.blockTxStatus[ 'txNode'] = self.nodeParams.config.nodeId # this node requested block transfer self.comm.blockTxStatus['status'] = TDMABlockTxStatus.pending cmdMsg = Command(TDMACmds['BlockTxRequestResponse'], { 'blockReqID': blockReqID, "accept": True }, [ TDMACmds['BlockTxRequestResponse'], 1, self.nodeParams.get_cmdCounter() ]).serialize(self.nodeParams.clock.getTime()) print(self.nodeParams.config.nodeId) self.nodeParams.nodeStatus[ 0].present = True # mark another node as present self.comm.populateBlockResponseList() # create block response list # Test acceptance marked assert (self.comm.processMsg(cmdMsg, args={ 'nodeStatus': self.nodeStatus, 'comm': self.comm, 'clock': self.nodeParams.clock }) == True) assert (self.comm.blockTxStatus['blockResponseList'][1] == True) # Test rejection marked time.sleep(0.01) cmdMsg = Command(TDMACmds['BlockTxRequestResponse'], { 'blockReqID': blockReqID, "accept": False }, [ TDMACmds['BlockTxRequestResponse'], 1, self.nodeParams.get_cmdCounter() ]).serialize(self.nodeParams.clock.getTime()) assert (self.comm.processMsg(cmdMsg, args={ 'nodeStatus': self.nodeStatus, 'comm': self.comm, 'clock': self.nodeParams.clock }) == True) assert (self.comm.blockTxStatus['blockResponseList'][1] == False)
# TestCmd type TestCmd = namedtuple('TestCmd', ['cmdData', 'body', 'header']) # Node configuration nodeId = 1 cmdCounter = 11 # Test commands dictionary testCmds = dict() ### NodeCmds # NodeCmds['NoOp'] cmdId = NodeCmds['NoOp'] cmdData = {} cmd = Command(cmdId, cmdData, [cmdId, nodeId, nodeParams.get_cmdCounter()]) testCmds.update({cmdId: cmd}) # NodeCmds['GCSCmd'] cmdId = NodeCmds['GCSCmd'] cmdData = {'destId': 0, 'mode': 1} cmd = Command(cmdId, cmdData, [cmdId, 0, nodeParams.get_cmdCounter()]) testCmds.update({cmdId: cmd}) # NodeCmds['ConfigRequest'] cmdId = NodeCmds['ConfigRequest'] cmdData = {'configHash': nodeParams.config.calculateHash()} cmd = Command(cmdId, cmdData, [cmdId, 0, nodeParams.get_cmdCounter()]) testCmds.update({cmdId: cmd}) # NodeCmds['ParamUpdate']
class TestNodeParams: def setup_method(self, method): # Create NodeConfig instance self.nodeConfig = NodeConfig(configFilePath) if method.__name__ != "test_init": self.nodeParams = NodeParams( config=self.nodeConfig) # Create NodeParams instance def test_init(self): """Test NodeParams init function.""" # Test that init function loads node configuration from file print("Testing init with configFile") nodeParams = NodeParams(configFile=configFilePath) assert (nodeParams.config.__dict__ == self.nodeConfig.__dict__) # Test that init function loads node configuration from provided config nodeParams = NodeParams(config=self.nodeConfig) assert (nodeParams.config.__dict__ == self.nodeConfig.__dict__) def test_getCmdCounter(self): """Test NodeParams command counter get function.""" # Test getting random command counter counter = self.nodeParams.get_cmdCounter() assert (counter >= 1) # value within range assert (counter <= 65536) # value within range # Test getting time-based command counter (NOTE: time-based counter commented out) #self.nodeParams.commStartTime = time.time() #time.sleep(0.5) #counter = self.nodeParams.get_cmdCounter() #assert(counter >= 0.5 * 1000) #assert(counter <= 0.6 * 1000) def test_checkNodeLinks(self): nodeId = self.nodeParams.config.nodeId - 1 # Test for direct link self.nodeParams.nodeStatus[2].present = True self.nodeParams.nodeStatus[2].lastMsgRcvdTime = time.time( ) - 0.90 * self.nodeParams.config.commConfig['linkTimeout'] self.nodeParams.checkNodeLinks() assert (self.nodeParams.linkStatus[nodeId][2] == LinkStatus.GoodLink) # Test for indirect link self.nodeParams.nodeStatus[2].present = False self.nodeParams.nodeStatus[2].updating = True self.nodeParams.checkNodeLinks() assert ( self.nodeParams.linkStatus[nodeId][2] == LinkStatus.IndirectLink) # Test for no link self.nodeParams.nodeStatus[2].present = False self.nodeParams.nodeStatus[2].updating = False self.nodeParams.checkNodeLinks() assert (self.nodeParams.linkStatus[nodeId][2] == LinkStatus.NoLink) def test_updateStatus(self): """Test updateStatus method of NodeParams.""" # Test without confirmed config self.nodeParams.updateStatus() assert (self.nodeParams.nodeStatus[self.nodeParams.config.nodeId - 1].status == 0) # Test with confirmed config self.nodeParams.configConfirmed = True self.nodeParams.updateStatus() assert (self.nodeParams.nodeStatus[self.nodeParams.config.nodeId - 1].status == 64) def test_loadConfig(self): """Test loadConfig method of NodeParams.""" # Create new config for update newConfig = NodeConfig(configFilePath) newConfigHash = newConfig.calculateHash() with open(configFilePath, "r") as jsonFile: configData = json.load(jsonFile) newConfig_pb = NodeConfig.toProtoBuf(configData).SerializeToString() # Test update method badHash = hashlib.sha1() assert (self.nodeParams.loadConfig(newConfig_pb, badHash) == False ) # test rejection with bad hash assert (self.nodeParams.newConfig == None) assert (self.nodeParams.loadConfig(newConfig_pb, newConfigHash) == True ) # test acceptance assert (self.nodeParams.newConfig != None) def test_updateConfig(self): """Test updateConfig method of NodeParams.""" # Load valid new config newConfig = NodeConfig(configFilePath) newConfigHash = newConfig.calculateHash() with open(configFilePath, "r") as jsonFile: configData = json.load(jsonFile) newConfig_pb = NodeConfig.toProtoBuf(configData).SerializeToString() self.nodeParams.loadConfig(newConfig_pb, newConfigHash) # Test successful loading self.nodeParams.config.FCBaudrate = newConfig.FCBaudrate / 2.0 # change a parameter to verify loading of new config assert (self.nodeParams.config.FCBaudrate != newConfig.FCBaudrate) assert (self.nodeParams.updateConfig() == True) assert (self.nodeParams.config.FCBaudrate == newConfig.FCBaudrate) assert (self.nodeParams.newConfig == None) # Expect no load when config missing assert (self.nodeParams.updateConfig() == False)
class TestTDMABlockTx: def setup_method(self, method): if testSerialPort: serialPort = serial.Serial(port=testSerialPort, baudrate=57600, timeout=0) else: serialPort = [] self.nodeParams = NodeParams(configFile=configFilePath) self.nodeParams.commStartTime = time.time() self.nodeParams.config.commConfig['transmitSlot'] = 1 self.radio = Radio( serialPort, { 'uartNumBytesToRead': self.nodeParams.config.uartNumBytesToRead, 'rxBufferSize': 2000 }) commProcessor = CommProcessor([TDMACmdProcessor, TestCmdProcessor], self.nodeParams) msgParser = SLIPMsgParser( {'parseMsgMax': self.nodeParams.config.parseMsgMax}) self.tdmaComm = TDMAComm(commProcessor, self.radio, msgParser, self.nodeParams) self.tdmaComm.nodeParams.frameStartTime = time.time() def test_populateBlockResponseList(self): """Test populateBlockResponseList method of TDMAComm.""" # Set node presence flags presentNodes = [1, 2, 3] for node in presentNodes: self.nodeParams.nodeStatus[node - 1].present = True self.tdmaComm.populateBlockResponseList() for node in presentNodes: assert (node in self.tdmaComm.blockTxStatus['blockResponseList']) def test_checkBlockResponse(self): """Test checkBlockResponse method of TDMAComm.""" # Set block response list self.tdmaComm.blockTxStatus['blockResponseList'] = { 1: None, 2: None, 3: None } # False response test self.tdmaComm.blockTxStatus['blockResponseList'][2] = False assert (self.tdmaComm.checkBlockResponse() == False) # Waiting for responses test self.tdmaComm.blockTxStatus['blockResponseList'][2] = True assert (self.tdmaComm.checkBlockResponse() == None) # True responses test for node in self.tdmaComm.blockTxStatus['blockResponseList']: self.tdmaComm.blockTxStatus['blockResponseList'][node] = True assert (self.tdmaComm.checkBlockResponse() == True) def test_sendDataBlock(self): """Test sendDataBlock method of TDMAComm.""" # Manually set frame start time self.tdmaComm.frameStartTime = time.time() dataBlock = b'1234567890' * 50 self.tdmaComm.sendDataBlock(dataBlock) # Check data stored and block tx request sent assert (self.tdmaComm.dataBlock == dataBlock) # data block stored header, msgContents = deserialize( self.tdmaComm.radio.txBuffer[1:-3], TDMACmds['BlockTxRequest'] ) # parse only raw message portions of output assert (msgContents['blockReqID'] == self.tdmaComm.blockTxStatus['blockReqID']) assert (msgContents['startTime'] == self.tdmaComm.blockTxStatus['startTime']) assert (msgContents['length'] == self.tdmaComm.blockTxStatus['length']) def test_monitorBlockTx(self): """Test monitorBlockTx method of TDMAComm.""" # Manually set frame start time self.tdmaComm.frameStartTime = time.time() ## Test block pending behavior # Check for block request response checks self.setupBlockRequest('tx') for node in self.tdmaComm.blockTxStatus['blockResponseList']: self.tdmaComm.blockTxStatus['blockResponseList'][node] = True self.tdmaComm.monitorBlockTx() assert (self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.confirmed) # Check for request timeout self.setupBlockRequest('tx') self.tdmaComm.frameStartTime += 0.5 * self.nodeParams.config.commConfig[ 'blockTxRequestTimeout'] * self.nodeParams.config.commConfig[ 'frameLength'] self.tdmaComm.monitorBlockTx() assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.pending) self.tdmaComm.frameStartTime += 0.5 * self.nodeParams.config.commConfig[ 'blockTxRequestTimeout'] * self.nodeParams.config.commConfig[ 'frameLength'] + 0.1 self.tdmaComm.monitorBlockTx() assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.false) # Check for cancel when not confirmed self.setupBlockRequest('rx') self.tdmaComm.frameStartTime = self.tdmaComm.blockTxStatus[ 'requestTime'] self.tdmaComm.frameStartTime += 0.5 * ( self.tdmaComm.blockTxStatus['startTime'] - self.tdmaComm.blockTxStatus['requestTime']) self.tdmaComm.monitorBlockTx() assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.pending) self.tdmaComm.frameStartTime += 0.5 * ( self.tdmaComm.blockTxStatus['startTime'] - self.tdmaComm.blockTxStatus['requestTime']) self.tdmaComm.monitorBlockTx() assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.false) ## Test block confirmed behavior # Check for change to active block tx status self.setupBlockRequest('rx') self.tdmaComm.blockTxStatus['status'] = TDMABlockTxStatus.confirmed self.tdmaComm.frameStartTime = self.tdmaComm.blockTxStatus[ 'requestTime'] self.tdmaComm.frameStartTime += 0.5 * ( self.tdmaComm.blockTxStatus['startTime'] - self.tdmaComm.blockTxStatus['requestTime']) self.tdmaComm.monitorBlockTx() assert (self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.confirmed) self.tdmaComm.frameStartTime += 0.5 * ( self.tdmaComm.blockTxStatus['startTime'] - self.tdmaComm.blockTxStatus['requestTime']) self.tdmaComm.monitorBlockTx() assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.active) # Check block tx status message sent self.tdmaComm.radio.txBuffer = bytearray() self.setupBlockRequest() self.tdmaComm.blockTxStatus['status'] = TDMABlockTxStatus.confirmed self.tdmaComm.frameStartTime = self.tdmaComm.blockTxStatus[ 'requestTime'] self.tdmaComm.monitorBlockTx() print(self.tdmaComm.radio.txBuffer) header, msgContents = deserialize(self.tdmaComm.radio.txBuffer, TDMACmds['BlockTxStatus']) assert (msgContents['blockReqID'] == self.tdmaComm.blockTxStatus['blockReqID']) assert (msgContents['startTime'] == self.tdmaComm.blockTxStatus['startTime']) assert (msgContents['length'] == self.tdmaComm.blockTxStatus['length']) ## Test block active behavior # Check for block end from time self.setupBlockTxActive() self.tdmaComm.monitorBlockTx() assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.active) self.tdmaComm.frameStartTime += self.tdmaComm.blockTxStatus[ 'length'] * self.nodeParams.config.commConfig['frameLength'] self.tdmaComm.monitorBlockTx() assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.false) # Check for block end from complete flag set self.setupBlockTxActive() self.tdmaComm.monitorBlockTx() assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.active) self.tdmaComm.blockTxStatus['blockTxComplete'] = True self.tdmaComm.monitorBlockTx() assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.false) # Check for block end from status set to nominal self.setupBlockTxActive() self.tdmaComm.monitorBlockTx() assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.active) self.nodeParams.tdmaStatus = TDMAStatus.nominal self.tdmaComm.monitorBlockTx() assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.false) def test_sendBlock(self): """Test sendBlock method of TDMAComm.""" dataBlock = b'1234567890' * 50 # Check for blockTxComplete set if no data block assert (self.tdmaComm.blockTxStatus['blockTxComplete'] == False) self.tdmaComm.sendBlock() assert (self.tdmaComm.blockTxStatus['blockTxComplete'] == True) ## Test that block is sent in appropriately sized chunks self.tdmaComm.blockTxStatus[ 'blockTxComplete'] = False # reset block tx complete flag self.nodeParams.config.commConfig['maxBlockTransferSize'] = int( len(dataBlock) / 2) + 1 # half of block + 1 self.tdmaComm.dataBlock = dataBlock self.tdmaComm.sendBlock() time.sleep(0.1) # Check that max block transfer sized chunk sent assert (self.tdmaComm.dataBlockPos == self.nodeParams.config.commConfig['maxBlockTransferSize']) self.tdmaComm.radio.readBytes(True) assert (self.tdmaComm.radio.bytesInRxBuffer == self.nodeParams.config.commConfig['maxBlockTransferSize']) assert (self.tdmaComm.blockTxStatus['blockTxComplete'] == False) # Check that rest of block sent self.tdmaComm.radio.clearRxBuffer() self.tdmaComm.sendBlock() time.sleep(0.1) assert (self.tdmaComm.dataBlockPos == 0) self.tdmaComm.radio.readBytes(True) assert ( self.tdmaComm.radio.bytesInRxBuffer == int(len(dataBlock) / 2) + 1 ) # half of block + END byte assert (self.tdmaComm.blockTxStatus['blockTxComplete'] == True) #def test_dumpBlockData(self): # """Test dumpBlockData method of TDMAComm.""" # self.tdmaComm.receivedBlockData = b'1234567890'*10 # self.tdmaComm.dumpBlockData('./') # assert(len(self.tdmaComm.receivedBlockData) == 0) def test_nominalTDMABlockTx(self): """Test TDMA block transfer sequence.""" # Force init self.tdmaComm.meshInited = True dataBlock = b'1234567890' * 50 self.nodeParams.config.commConfig['maxBlockTransferSize'] = int( len(dataBlock) / 2) + 1 self.tdmaComm.frameStartTime = time.time() # Set present nodes self.nodeParams.nodeStatus[0].present = True self.nodeParams.nodeStatus[1].present = True # Request sending of data block self.initiateBlockTransmit() assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.pending) ## Execute TDMA comm and monitor block transfer process # Check for status change to pending self.tdmaComm.frameStartTime += self.nodeParams.config.commConfig[ 'frameLength'] self.tdmaComm.execute(self.tdmaComm.frameStartTime) assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.pending) # "Send" positive request response from one node cmdMsg = Command( TDMACmds['BlockTxRequestResponse'], { 'blockReqID': self.tdmaComm.blockTxStatus['blockReqID'], "accept": True }, [ TDMACmds['BlockTxRequestResponse'], 1, self.nodeParams.get_cmdCounter() ]).serialize(self.nodeParams.clock.getTime()) self.tdmaComm.commProcessor.processMsg(cmdMsg, args={ 'nodeStatus': self.nodeParams.nodeStatus, 'comm': self.tdmaComm, 'clock': self.nodeParams.clock }) print(self.tdmaComm.blockTxStatus['blockResponseList']) assert (self.tdmaComm.blockTxStatus['blockResponseList'][1] == True ) # response list updated self.tdmaComm.frameStartTime += self.nodeParams.config.commConfig[ 'frameLength'] self.tdmaComm.execute(self.tdmaComm.frameStartTime) assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.pending) # Send remaining positive responses and check for update to confirmed status time.sleep(0.1) cmdMsg = Command( TDMACmds['BlockTxRequestResponse'], { 'blockReqID': self.tdmaComm.blockTxStatus['blockReqID'], "accept": True }, [ TDMACmds['BlockTxRequestResponse'], 2, self.nodeParams.get_cmdCounter() ]).serialize(self.nodeParams.clock.getTime()) self.tdmaComm.commProcessor.processMsg(cmdMsg, args={ 'nodeStatus': self.nodeParams.nodeStatus, 'comm': self.tdmaComm, 'clock': self.nodeParams.clock }) self.tdmaComm.frameStartTime += self.nodeParams.config.commConfig[ 'frameLength'] self.tdmaComm.execute(self.tdmaComm.frameStartTime) assert (self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.confirmed) # Advance to block start time self.tdmaComm.radio.txBuffer = bytearray() # clear tx buffer self.tdmaComm.frameStartTime = self.tdmaComm.blockTxStatus['startTime'] self.tdmaComm.execute(self.tdmaComm.frameStartTime) time.sleep(0.1) self.tdmaComm.radio.readBytes(True) assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.active ) # block started assert (self.nodeParams.tdmaStatus == TDMAStatus.blockTx ) # tdma status updated assert (self.tdmaComm.radio.bytesInRxBuffer >= self.nodeParams.config.commConfig['maxBlockTransferSize']) assert (self.tdmaComm.blockTxStatus['blockTxComplete'] == False) # Continue sending and check for block tx complete flag self.tdmaComm.frameStartTime += self.nodeParams.config.commConfig[ 'frameLength'] self.tdmaComm.execute(self.tdmaComm.frameStartTime) assert (self.tdmaComm.blockTxStatus['blockTxComplete'] == True) # Check for transition back to nominal TDMA self.tdmaComm.frameStartTime += self.nodeParams.config.commConfig[ 'frameLength'] self.tdmaComm.execute(self.tdmaComm.frameStartTime) assert (self.nodeParams.tdmaStatus == TDMAStatus.nominal) assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.false) def test_rejectedTDMABlockTx(self): """Test rejecting block transmit request.""" # Force init self.tdmaComm.meshInited = True # Start block transmit self.initiateBlockTransmit() assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.pending) # "Send" negative response from other node time.sleep(0.01) cmdMsg = Command( TDMACmds['BlockTxRequestResponse'], { 'blockReqID': self.tdmaComm.blockTxStatus['blockReqID'], "accept": False }, [ TDMACmds['BlockTxRequestResponse'], 2, self.nodeParams.get_cmdCounter() ]).serialize(self.nodeParams.clock.getTime()) self.tdmaComm.commProcessor.processMsg(cmdMsg, args={ 'nodeStatus': self.nodeParams.nodeStatus, 'comm': self.tdmaComm, 'clock': self.nodeParams.clock }) self.tdmaComm.frameStartTime += self.nodeParams.config.commConfig[ 'frameLength'] self.tdmaComm.execute(self.tdmaComm.frameStartTime) assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.false ) # request cancelled def test_blockTxEndTime(self): """Test block transmit ended due to block length reached.""" # Force init self.tdmaComm.meshInited = True # Start block transmit self.initiateBlockTransmit() self.tdmaComm.blockTxStatus['status'] = TDMABlockTxStatus.confirmed self.tdmaComm.frameStartTime = self.tdmaComm.blockTxStatus['startTime'] self.tdmaComm.execute(self.tdmaComm.frameStartTime) assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.active) assert (self.nodeParams.tdmaStatus == TDMAStatus.blockTx) # Advance to block end and check that block terminated self.tdmaComm.frameStartTime += self.nodeParams.config.commConfig[ 'frameLength'] * self.tdmaComm.blockTxStatus['length'] self.tdmaComm.execute(self.nodeParams.frameStartTime) assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.false) assert (self.nodeParams.tdmaStatus == TDMAStatus.nominal) def test_processSendBlockDataCmd(self): """Test processing of SendBlockData command from GCS.""" self.tdmaComm.frameStartTime = 0.0 # set a frame start time since main execution is bypassed # Create TestCmds['SendBlockData'] command cmdMsg = Command(TestCmds['SendDataBlock'], { 'destId': self.nodeParams.config.nodeId }, [TestCmds['SendDataBlock'], 0, self.nodeParams.get_cmdCounter()]).serialize( self.nodeParams.clock.getTime()) # Process command and check result self.tdmaComm.commProcessor.processMsg(cmdMsg, args={ 'nodeStatus': self.nodeParams.nodeStatus, 'comm': self.tdmaComm }) assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.pending) assert (self.tdmaComm.dataBlock == b'1234567890' * 100) def test_nodePresenceUpdate(self): """Test that node presence does not timeout during block transmits.""" self.nodeParams.frameStartTime = time.time() self.tdmaComm.blockTxStatus['status'] = TDMABlockTxStatus.active self.tdmaComm.blockTxStatus[ 'txNode'] = self.nodeParams.config.nodeId + 1 self.tdmaComm.blockTxStatus['length'] = 10 self.tdmaComm.blockTxStatus[ 'startTime'] = self.nodeParams.frameStartTime self.nodeParams.tdmaStatus = TDMAStatus.blockTx # Set present nodes self.nodeParams.nodeStatus[1].present = True self.nodeParams.nodeStatus[2].present = True # Propagate time and check that node update time is updated self.tdmaComm.execute(0.0) assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.active) assert ((self.nodeParams.nodeStatus[1].lastStateUpdateTime - time.time()) <= 0.001) time.sleep(5.0) self.tdmaComm.execute(0.0) assert ( self.tdmaComm.blockTxStatus['status'] == TDMABlockTxStatus.active) assert ((self.nodeParams.nodeStatus[1].lastStateUpdateTime - time.time()) <= 0.001) def initiateBlockTransmit(self): """Initiate block transmit process.""" dataBlock = b'1234567890' * 50 self.nodeParams.config.commConfig['maxBlockTransferSize'] = int( len(dataBlock) / 2) + 1 self.tdmaComm.frameStartTime = time.time() # Set present nodes self.nodeParams.nodeStatus[0].present = True self.nodeParams.nodeStatus[1].present = True # Request sending of data block self.tdmaComm.sendDataBlock(dataBlock) def setupBlockTxActive(self): self.setupBlockRequest('tx') self.tdmaComm.blockTxStatus['status'] = TDMABlockTxStatus.active self.nodeParams.tdmaStatus = TDMAStatus.blockTx self.tdmaComm.frameStartTime = self.tdmaComm.blockTxStatus['startTime'] def setupBlockRequest(self, role='tx'): self.tdmaComm.resetBlockTxStatus() currentTime = time.time() self.tdmaComm.blockTxStatus['blockReqID'] = 5 self.tdmaComm.blockTxStatus['startTime'] = int( currentTime + self.nodeParams.config.commConfig['minBlockTxDelay'] * self.nodeParams.config.commConfig['frameLength']) self.tdmaComm.blockTxStatus['length'] = 5 # Set block response list self.tdmaComm.blockTxStatus['blockResponseList'] = { 1: None, 2: None, 3: None } if role == 'tx': # This node transmitting self.tdmaComm.blockTxStatus[ 'txNode'] = self.nodeParams.config.nodeId else: self.tdmaComm.blockTxStatus[ 'txNode'] = self.nodeParams.config.nodeId + 1 self.tdmaComm.blockTxStatus['status'] = TDMABlockTxStatus.pending self.tdmaComm.blockTxStatus['requestTime'] = currentTime
class TestTDMACmdProcessor: def setup_method(self, method): self.nodeStatus = [NodeState(i + 1) for i in range(5)] self.nodeParams = NodeParams(configFile=configFilePath) self.commProcessor = CommProcessor([TDMACmdProcessor], self.nodeParams) msgParser = SLIPMsgParser( {'parseMsgMax': self.nodeParams.config.parseMsgMax}) radio = Radio( [], { 'uartNumBytesToRead': self.nodeParams.config.uartNumBytesToRead, 'rxBufferSize': 2000 }) self.comm = TDMAComm(self.commProcessor, radio, msgParser, self.nodeParams) def test_validateBlockTxRequest(self): """Test validateBlockTxRequest method of TDMACmdProcessor.""" # Test request rejected if start time passed contents = { 'startTime': time.time() - 1.0, 'length': self.nodeParams.config.commConfig['maxTxBlockSize'] } assert (validateBlockTxRequest(contents, [], self.nodeParams) == False) # Test request rejected if block too long contents = { 'startTime': time.time() + 1.0, 'length': self.nodeParams.config.commConfig['maxTxBlockSize'] + 1 } assert (validateBlockTxRequest(contents, [], self.nodeParams) == False) # Test for request acceptance contents = { 'startTime': time.time() + 1.0, 'length': self.nodeParams.config.commConfig['maxTxBlockSize'] } assert (validateBlockTxRequest(contents, [], self.nodeParams) == True) def test_processMsg(self): """Test processMsg method of TDMACmdProcessor.""" # Test processing of all TDMACmds for cmdId in cmdsToTest: cmdMsg = packHeader(testCmds[cmdId].header) + testCmds[cmdId].body self.commProcessor.processMsg(cmdMsg, args={ 'nodeStatus': self.nodeStatus, 'comm': self.comm, 'clock': self.nodeParams.clock }) if cmdId == TDMACmds['TimeOffset']: sourceId = testCmds[cmdId].header['header']['sourceId'] assert ( self.nodeStatus[sourceId - 1].timeOffset == testCmds[cmdId].body[0] / 100.0) elif cmdId == TDMACmds['TimeOffsetSummary']: for i in range(len(testCmds[cmdId].cmdData['nodeStatus'])): assert (self.nodeStatus[i].timeOffset == testCmds[cmdId]. cmdData['nodeStatus'][i].timeOffset) elif cmdId == TDMACmds['MeshStatus']: assert (self.nodeParams.commStartTime == testCmds[cmdId].cmdData['commStartTimeSec']) # Resend and test that commStartTime is not updated once it has previously been set cmdId = TDMACmds['MeshStatus'] self.nodeParams.commStartTime = testCmds[cmdId].cmdData[ 'commStartTimeSec'] - 1 cmdMsg = packHeader(testCmds[cmdId].header) + testCmds[cmdId].body self.commProcessor.processMsg(cmdMsg, args={ 'nodeStatus': self.nodeStatus, 'comm': self.comm, 'clock': self.nodeParams.clock }) assert (self.nodeParams.commStartTime != testCmds[cmdId].cmdData['commStartTimeSec']) def test_blockTxCmdsProcessing(self): """Test processing of block transmit related commands.""" self.nodeParams.commStartTime = self.nodeParams.clock.getTime() - 1.0 blockReqID = random.randint(1, 255) # just a random "unique" number startTime = int(self.nodeParams.clock.getTime() + 10.0) length = self.nodeParams.config.commConfig['maxTxBlockSize'] txNode = 1 ## TDMACmds['BlockTxRequest'] cmdMsg = Command(TDMACmds['BlockTxRequest'], { 'blockReqID': blockReqID, 'startTime': startTime, 'length': length }, [ TDMACmds['BlockTxRequest'], txNode, self.nodeParams.get_cmdCounter() ]).serialize(self.nodeParams.clock.getTime()) # Process and check results self.commProcessor.processMsg(cmdMsg, args={ 'nodeStatus': self.nodeStatus, 'comm': self.comm, 'clock': self.nodeParams.clock }) assert (len(self.comm.radio.txBuffer) == calcsize( CmdDict[TDMACmds['BlockTxRequestResponse']].packFormat) + calcsize(headers['NodeHeader']['format'])) # response sent assert (self.comm.blockTxStatus['blockReqID'] == blockReqID) assert (self.comm.blockTxStatus['status'] == TDMABlockTxStatus.pending) assert (self.comm.blockTxStatus['txNode'] == txNode) assert (self.comm.blockTxStatus['startTime'] == startTime) assert (self.comm.blockTxStatus['length'] == length) ## TDMACmds['BlockTxConfirmed'] time.sleep(0.01) cmdMsg = Command(TDMACmds['BlockTxConfirmed'], { 'blockReqID': blockReqID }, [ TDMACmds['BlockTxConfirmed'], txNode, self.nodeParams.get_cmdCounter() ]).serialize(self.nodeParams.clock.getTime()) self.commProcessor.processMsg(cmdMsg, args={ 'nodeStatus': self.nodeStatus, 'comm': self.comm, 'clock': self.nodeParams.clock }) assert ( self.comm.blockTxStatus['status'] == TDMABlockTxStatus.confirmed ) # status updated to confirmed ## TDMACmds['BlockTxStatus'] self.comm.resetBlockTxStatus() time.sleep(0.01) cmdMsg = Command(TDMACmds['BlockTxStatus'], { 'blockReqID': blockReqID, 'startTime': startTime, 'length': length }, [ TDMACmds['BlockTxStatus'], txNode, self.nodeParams.get_cmdCounter() ]).serialize(self.nodeParams.clock.getTime()) # Check status updated self.commProcessor.processMsg(cmdMsg, args={ 'nodeStatus': self.nodeStatus, 'comm': self.comm, 'clock': self.nodeParams.clock }) assert (len(self.comm.radio.txBuffer) == calcsize( CmdDict[TDMACmds['BlockTxRequestResponse']].packFormat) + calcsize(headers['NodeHeader']['format'])) # response sent assert (self.comm.blockTxStatus['blockReqID'] == blockReqID) assert ( self.comm.blockTxStatus['status'] == TDMABlockTxStatus.confirmed) assert (self.comm.blockTxStatus['txNode'] == txNode) assert (self.comm.blockTxStatus['startTime'] == startTime) assert (self.comm.blockTxStatus['length'] == length) # Check status updated to confirmed if only pending time.sleep(0.01) cmdMsg = Command(TDMACmds['BlockTxStatus'], { 'blockReqID': blockReqID, 'startTime': startTime, 'length': length }, [ TDMACmds['BlockTxStatus'], txNode, self.nodeParams.get_cmdCounter() ]).serialize(self.nodeParams.clock.getTime()) # update command counter self.comm.blockTxStatus['status'] = TDMABlockTxStatus.pending self.commProcessor.processMsg(cmdMsg, args={ 'nodeStatus': self.nodeStatus, 'comm': self.comm, 'clock': self.nodeParams.clock }) assert ( self.comm.blockTxStatus['status'] == TDMABlockTxStatus.confirmed) ## TDMACmds['BlockTxRequestResponse'] time.sleep(0.01) self.comm.resetBlockTxStatus() self.comm.blockTxStatus[ 'txNode'] = self.nodeParams.config.nodeId # this node requested block transfer self.comm.blockTxStatus['status'] = TDMABlockTxStatus.pending cmdMsg = Command(TDMACmds['BlockTxRequestResponse'], { 'blockReqID': blockReqID, "accept": True }, [ TDMACmds['BlockTxRequestResponse'], 1, self.nodeParams.get_cmdCounter() ]).serialize(self.nodeParams.clock.getTime()) print(self.nodeParams.config.nodeId) self.nodeParams.nodeStatus[ 0].present = True # mark another node as present self.comm.populateBlockResponseList() # create block response list # Test acceptance marked self.commProcessor.processMsg(cmdMsg, args={ 'nodeStatus': self.nodeStatus, 'comm': self.comm, 'clock': self.nodeParams.clock }) assert (self.comm.blockTxStatus['blockResponseList'][1] == True) # Test rejection marked time.sleep(0.01) cmdMsg = Command(TDMACmds['BlockTxRequestResponse'], { 'blockReqID': blockReqID, "accept": False }, [ TDMACmds['BlockTxRequestResponse'], 1, self.nodeParams.get_cmdCounter() ]).serialize(self.nodeParams.clock.getTime()) self.commProcessor.processMsg(cmdMsg, args={ 'nodeStatus': self.nodeStatus, 'comm': self.comm, 'clock': self.nodeParams.clock }) assert (self.comm.blockTxStatus['blockResponseList'][1] == False)
class TestNodeParams: def setup_method(self, method): # Create NodeConfig instance self.nodeConfig = NodeConfig(configFilePath) if method.__name__ != "test_init": self.nodeParams = NodeParams(config=self.nodeConfig) # Create NodeParams instance def test_init(self): """Test NodeParams init function.""" # Test that init function loads node configuration from file print("Testing init with configFile") nodeParams = NodeParams(configFile=configFilePath) assert(nodeParams.config.__dict__ == self.nodeConfig.__dict__) # Test that init function loads node configuration from provided config nodeParams = NodeParams(config=self.nodeConfig) assert(nodeParams.config.__dict__ == self.nodeConfig.__dict__) def test_getCmdCounter(self): """Test NodeParams command counter get function.""" # Test getting random command counter counter = self.nodeParams.get_cmdCounter() assert(counter >= 1) # value within range assert(counter <= 65536) # value within range # Test getting time-based command counter (NOTE: time-based counter commented out) #self.nodeParams.commStartTime = time.time() #time.sleep(0.5) #counter = self.nodeParams.get_cmdCounter() #assert(counter >= 0.5 * 1000) #assert(counter <= 0.6 * 1000) def test_checkNodeLinks(self): nodeId = self.nodeParams.config.nodeId - 1 # Test for direct link self.nodeParams.nodeStatus[2].present = True self.nodeParams.nodeStatus[2].lastMsgRcvdTime = time.time() - 0.90 * self.nodeParams.config.commConfig['linkTimeout'] self.nodeParams.checkNodeLinks() assert(self.nodeParams.linkStatus[nodeId][2] == LinkStatus.GoodLink) # Test for indirect link self.nodeParams.nodeStatus[2].present = False self.nodeParams.nodeStatus[2].updating = True self.nodeParams.checkNodeLinks() assert(self.nodeParams.linkStatus[nodeId][2] == LinkStatus.IndirectLink) # Test for no link self.nodeParams.nodeStatus[2].present = False self.nodeParams.nodeStatus[2].updating = False self.nodeParams.checkNodeLinks() assert(self.nodeParams.linkStatus[nodeId][2] == LinkStatus.NoLink) def test_updateStatus(self): """Test updateStatus method of NodeParams.""" # Test without confirmed config self.nodeParams.updateStatus() assert(self.nodeParams.nodeStatus[self.nodeParams.config.nodeId-1].status == 0) # Test with confirmed config self.nodeParams.configConfirmed = True self.nodeParams.updateStatus() assert(self.nodeParams.nodeStatus[self.nodeParams.config.nodeId-1].status == 64)