예제 #1
0
    def getBlockTxPacket(self):
        # Check for block data
        #if (self.blockTxData == None): # no data to send
        #    return None

        # Create mesh packet from next chunk of block data
        if (self.blockTx.dataLoc >= len(self.blockTx.data)): # no packets remaining
            return None
        elif (len(self.blockTx.data) > self.blockTx.dataLoc + self.nodeParams.config.commConfig['blockTxPacketSize']): 
            newBlockTxDataLoc = self.blockTx.dataLoc + int(self.nodeParams.config.commConfig['blockTxPacketSize'])
            blockDataChunk = self.blockTx.data[self.blockTx.dataLoc:newBlockTxDataLoc]
            self.blockTx.dataLoc = newBlockTxDataLoc
        else: # send remainder of data block
            blockDataChunk = self.blockTx.data[self.blockTx.dataLoc:]
            self.blockTx.dataLoc = len(self.blockTx.data) # reached end of data block
       
        # Generate new packet command
        self.blockTx.packetNum += 1
        blockDataCmd = Command(TDMACmds['BlockData'], {'blockReqId': self.blockTx.reqId, 'packetNum': self.blockTx.packetNum, 'dataLength': len(blockDataChunk), 'data': blockDataChunk}, [TDMACmds['BlockData'], self.nodeParams.config.nodeId])
        blockDataSerialized = self.tdmaCmdParser.encodeMsg(blockDataCmd.serialize(self.nodeParams.clock.getTime()))
 
        blockPacket = self.createMeshPacket(self.blockTx.destId, b'', blockDataSerialized, self.nodeParams.config.nodeId, BLOCK_TX_MSG)
                    
        print("Node " + str(self.nodeParams.config.nodeId) + " - Sending block transmit packet", self.blockTx.packetNum, ". Length-", len(blockPacket))

        return blockPacket
예제 #2
0
 def test_serialize(self):
     """Test serialization of command."""
     command = Command(self.cmdId, self.cmdData, self.header)
     
     serMsg = command.serialize(0)
     assert(len(serMsg) == (calcsize(headers[CmdDict[self.cmdId].header]['format']) + calcsize(CmdDict[self.cmdId].packFormat))) # serial message the correct length
         
예제 #3
0
    def test_processMeshMsgs(self):
        """Test processMeshMsgs method of TDMAComm."""
        commStartTime = int(time.time())
        cmd = Command(TDMACmds['MeshStatus'], {'commStartTimeSec': commStartTime, 'status': TDMAStatus.nominal}, [TDMACmds['MeshStatus'], self.nodeParams.config.nodeId])
        encodedCmd = self.tdmaComm.tdmaCmdParser.encodeMsg(cmd.serialize())

        # Verify pre-test conditions
        assert(self.tdmaComm.commStartTime == None)
        self.tdmaComm.processMeshMsgs(encodedCmd)
        assert(self.tdmaComm.commStartTime == commStartTime)
예제 #4
0
    def sendDataBlock(self, dataBlock):
        """Begins block transfer process."""

        # Calculate block tx parameters
        length = int(
            ceil(
                len(dataBlock) /
                self.nodeParams.config.commConfig['maxBlockTransferSize']))
        if length > self.nodeParams.config.commConfig['maxTxBlockSize']:
            # Too much data to transmit
            return False

        startTime = int(self.frameStartTime +
                        self.nodeParams.config.commConfig['frameLength'] *
                        self.nodeParams.config.commConfig['minBlockTxDelay'])

        # Store data block
        self.dataBlock = dataBlock

        # Populate response list
        self.populateBlockResponseList()

        # Send block tx request
        blockReqID = random.randint(1, 255)  # just a random "unique" number
        blockTxCmd = Command(TDMACmds['BlockTxRequest'], {
            'blockReqID': blockReqID,
            'startTime': startTime,
            'length': length
        }, [
            TDMACmds['BlockTxRequest'], self.nodeParams.config.nodeId,
            self.nodeParams.get_cmdCounter()
        ])
        self.bufferTxMsg(blockTxCmd.serialize(self.nodeParams.clock.getTime()))

        # Update blockTxStatus
        self.blockTxStatus['blockReqID'] = blockReqID
        self.blockTxStatus['startTime'] = startTime
        self.blockTxStatus['length'] = length
        self.blockTxStatus['requestTime'] = self.frameStartTime
        self.blockTxStatus['txNode'] = self.nodeParams.config.nodeId
        self.blockTxStatus['status'] = TDMABlockTxStatus.pending

        return True
예제 #5
0
    def test_packageMeshPacket(self):
        """Test packageMeshPacket method of TDMAComm."""
        packetHeaderLen = 8

        # Test no send of empty message
        msgBytes = b''
        destId = 5
        assert(len(self.tdmaComm.packageMeshPacket(destId, msgBytes)) == 0)
        
        # Confirm packet structure
        msgBytes = b'1234567890'
        packet = self.tdmaComm.packageMeshPacket(destId, msgBytes)
        assert(len(packet) == packetHeaderLen + len(msgBytes))
        packetHeader = struct.unpack('<BBHHH', packet[0:packetHeaderLen])        
        assert(packetHeader[0] == self.tdmaComm.nodeParams.config.nodeId)
        assert(packetHeader[1] == destId)
        assert(packetHeader[2] == 0) # no admin bytes
        assert(packetHeader[3] == len(msgBytes))
 
        # Test sending of periodic TDMA commands (broadcast message only)
        cmd = Command(TDMACmds['MeshStatus'], {'commStartTimeSec': int(time.time()), 'status': TDMAStatus.nominal}, [TDMACmds['MeshStatus'], self.nodeParams.config.nodeId])
        encodedCmd = self.tdmaComm.tdmaCmdParser.encodeMsg(cmd.serialize())
        self.tdmaComm.tdmaCmds = dict()
        self.tdmaComm.tdmaCmds[TDMACmds['MeshStatus']] = cmd

        destId = 0 # broadcast message
        packet = self.tdmaComm.packageMeshPacket(destId, msgBytes)
        assert(len(packet) == packetHeaderLen + len(encodedCmd) + len(msgBytes))
        assert(packet[packetHeaderLen:packetHeaderLen+len(encodedCmd)] == encodedCmd)
        assert(packet[packetHeaderLen+len(encodedCmd):] == msgBytes)

        # Confirm admin bytes still sent with zero length message bytes
        msgBytes = b''
        self.tdmaComm.tdmaCmds[TDMACmds['MeshStatus']] = cmd
        packet = self.tdmaComm.packageMeshPacket(destId, msgBytes)
        assert(len(packet) == packetHeaderLen + len(encodedCmd))
        assert(packet[packetHeaderLen:packetHeaderLen+len(encodedCmd)] == encodedCmd)
예제 #6
0
    def monitorBlockTx(self):
        """Monitors current status of block transmit."""
        if self.blockTxStatus['status'] == TDMABlockTxStatus.false:
            return

        elif self.blockTxStatus[
                'status'] == TDMABlockTxStatus.pending:  # monitor pending block request
            if self.blockTxStatus[
                    'txNode'] == self.nodeParams.config.nodeId:  # this node requested block tx
                # Check block request responses
                response = self.checkBlockResponse()
                if response == True:
                    # Confirm block tx
                    blockConfirmCmd = Command(
                        TDMACmds['BlockTxConfirmed'],
                        {'blockReqID': self.blockTxStatus['blockReqID']}, [
                            TDMACmds['BlockTxConfirmed'],
                            self.nodeParams.config.nodeId,
                            self.nodeParams.get_cmdCounter()
                        ])
                    self.radio.bufferTxMsg(
                        blockConfirmCmd.serialize(
                            self.nodeParams.clock.getTime()))
                    self.blockTxStatus['status'] = TDMABlockTxStatus.confirmed
                    return

                elif response == False:
                    # Cancel request
                    self.resetBlockTxStatus()
                    return

                # Check for request timeout
                if (self.frameStartTime - self.blockTxStatus['requestTime']
                    ) > self.nodeParams.config.commConfig[
                        'blockTxRequestTimeout'] * self.nodeParams.config.commConfig[
                            'frameLength']:
                    # Request timed out - reset status
                    self.resetBlockTxStatus()
                    return
            if self.frameStartTime >= self.blockTxStatus[
                    'startTime']:  # no block confirmed received
                # Cancel pending block
                self.resetBlockTxStatus()
                return

        elif self.blockTxStatus['status'] == TDMABlockTxStatus.confirmed:
            # Check for block start
            if self.frameStartTime >= self.blockTxStatus[
                    'startTime']:  # start block
                self.blockTxStatus['status'] = TDMABlockTxStatus.active
                self.nodeParams.tdmaStatus = TDMAStatus.blockTx

            # Send block transmit status message
            if self.blockTxStatus['txNode'] == self.nodeParams.config.nodeId:
                blockStatusCmd = Command(
                    TDMACmds['BlockTxStatus'], {
                        'blockReqID': self.blockTxStatus['blockReqID'],
                        'startTime': self.blockTxStatus['startTime'],
                        'length': self.blockTxStatus['length']
                    }, [
                        TDMACmds['BlockTxStatus'],
                        self.nodeParams.config.nodeId,
                        self.nodeParams.get_cmdCounter()
                    ])
                self.radio.bufferTxMsg(
                    blockStatusCmd.serialize(self.nodeParams.clock.getTime()))

        elif self.blockTxStatus[
                'status'] == TDMABlockTxStatus.active:  # block transmit in progress
            # Check for end of block transmit
            if self.blockTxStatus['blockTxComplete'] or (
                    self.frameStartTime - self.blockTxStatus['startTime']
            ) >= self.blockTxStatus['length'] * self.nodeParams.config.commConfig[
                    'frameLength'] or self.nodeParams.tdmaStatus == TDMAStatus.nominal:
                # Block transmit ended - reset status
                self.resetBlockTxStatus()
예제 #7
0
    def test_sendMsg(self):
        """Test sendMsg method of TDMAComm."""
        # Test send only when conditions met
        self.tdmaComm.enabled = False
        self.tdmaComm.tdmaMode = TDMAMode.receive
        self.tdmaComm.transmitComplete = False
        assert(len(self.tdmaComm.radio.txBuffer) == 0)
        self.tdmaComm.radio.bufferTxMsg(b'12345')
        assert(len(self.tdmaComm.radio.txBuffer) > 0)
        self.tdmaComm.sendMsg()
        assert(len(self.tdmaComm.radio.txBuffer) > 0) # message not sent
        assert(self.tdmaComm.transmitComplete == False)
        self.tdmaComm.enabled = True
        self.tdmaComm.sendMsg()
        assert(len(self.tdmaComm.radio.txBuffer) > 0) # message still not sent
        self.tdmaComm.tdmaMode = TDMAMode.transmit
        self.tdmaComm.sendMsg()
        assert(len(self.tdmaComm.radio.txBuffer) == 0) # message sent
        

        # Test transmission of periodic commands
        #serBytes = self.tdmaComm.radio.getRxBytes() # clear incoming bytes
        #assert(len(self.tdmaComm.cmdBuffer) == 0)
        #assert(len(self.tdmaComm.cmdRelayBuffer) == 0)
        #cmd = Command(TDMACmds['MeshStatus'], {'commStartTimeSec': int(time.time()), 'status': TDMAStatus.nominal}, [TDMACmds['MeshStatus'], self.nodeParams.config.nodeId])
        #self.tdmaComm.tdmaCmds[TDMACmds['MeshStatus']] = cmd
        #self.tdmaComm.sendMsg()
        #time.sleep(0.1)
        #self.tdmaComm.readBytes()
        #serBytes = self.tdmaComm.radio.getRxBytes()
        #assert(len(serBytes) > 0)
        #assert(serBytes[-1:] == HDLC_END_TDMA) # check for end of message indicator

        # Test command relay buffer
        testMsg = b'1234567890'
        self.tdmaComm.radio.clearRxBuffer()
        self.tdmaComm.cmdRelayBuffer = testMsg
        self.tdmaComm.sendMsg()
        time.sleep(0.1)
        self.tdmaComm.readBytes()
        serBytes = self.tdmaComm.radio.getRxBytes()
        assert(testMsg in serBytes)
       
        # Test command buffer
        self.tdmaComm.radio.clearRxBuffer()
        testCmd = {'bytes': b'12345'}
        self.tdmaComm.cmdBuffer['key1'] = testCmd
        self.tdmaComm.sendMsg()
        assert(len(self.tdmaComm.cmdBuffer) == 0) # command buffer flushed out
        time.sleep(0.1)
        self.tdmaComm.readBytes()
        assert(len(self.tdmaComm.radio.getRxBytes()) > 0)

        ## Test meshQueue processing
        # Test no output for empty queue
        self.tdmaComm.sendMsg()
        time.sleep(0.1)
        self.tdmaComm.readBytes()
        assert(len(self.tdmaComm.radio.getRxBytes()) > 0) # nothing sent when meshQueue is empty (and no periodic commands)

        # Test broadcast message output
        cmd = Command(TDMACmds['MeshStatus'], {'commStartTimeSec': int(time.time()), 'status': TDMAStatus.nominal}, [TDMACmds['MeshStatus'], self.nodeParams.config.nodeId])
        self.tdmaComm.tdmaCmds[TDMACmds['MeshStatus']] = cmd
        encodedCmd = self.tdmaComm.tdmaCmdParser.encodeMsg(cmd.serialize())
        self.tdmaComm.sendMsg()
        time.sleep(0.1)
        self.tdmaComm.readBytes()
        assert(len(self.tdmaComm.radio.getRxBytes()) > 0) # message sent when periodic commands pending
        self.tdmaComm.parseMsgs()
        assert(len(self.tdmaComm.msgParser.parsedMsgs) == 1)
        packetHeader = struct.unpack('<BBHHH', self.tdmaComm.msgParser.parsedMsgs[0][0:8])
        assert(packetHeader[1] == 0) # broadcast message
        assert(encodedCmd in self.tdmaComm.msgParser.parsedMsgs[0]) # tdmaCmds included in message

        # Test destination specific output
        self.nodeParams.config.commConfig['recvAllMsgs'] = True # receive all messages, regardless of dest
        msg1 = b'1234567890'
        msg1Dest = 3
        msg2 = b'0987654321'
        msg2Dest = 5
        self.tdmaComm.meshQueueIn[msg1Dest] = msg1
        self.tdmaComm.meshQueueIn[msg2Dest] = msg2 
        self.tdmaComm.sendMsg()
        time.sleep(0.1)
        self.tdmaComm.readBytes()
        assert(len(self.tdmaComm.radio.getRxBytes()) > 0)
        self.tdmaComm.processMsgs()
        assert(len(self.tdmaComm.hostBuffer) > 0)
        assert(msg1 in self.tdmaComm.hostBuffer)
        assert(msg2 in self.tdmaComm.hostBuffer)

        # Test without receiving messages for other nodes
        self.tdmaComm.hostBuffer = bytearray()
        self.nodeParams.config.commConfig['recvAllMsgs'] = False
        msg1Dest = self.nodeParams.config.nodeId
        self.tdmaComm.meshQueueIn[msg1Dest] = msg1
        self.tdmaComm.meshQueueIn[msg2Dest] = msg2 
        self.tdmaComm.sendMsg()
        time.sleep(0.1)
        self.tdmaComm.readBytes()
        assert(len(self.tdmaComm.radio.getRxBytes()) > 0)
        self.tdmaComm.processMsgs()
        assert(len(self.tdmaComm.hostBuffer) > 0)
        assert(self.tdmaComm.hostBuffer == msg1)
예제 #8
0
    def test_processMsgs(self):
        """Test processMsgs method of TDMAComm."""
        commStartTime = int(time.time())
        cmd = Command(TDMACmds['MeshStatus'], {'commStartTimeSec': commStartTime, 'status': TDMAStatus.nominal}, [TDMACmds['MeshStatus'], self.nodeParams.config.nodeId])
        encodedCmd = self.tdmaComm.tdmaCmdParser.encodeMsg(cmd.serialize())
        self.tdmaComm.tdmaCmds = dict()
        self.tdmaComm.tdmaCmds[TDMACmds['MeshStatus']] = cmd
        payloadBytes = b'1234567890'
        
        # Verify pre-test conditions
        assert(len(self.tdmaComm.cmdRelayBuffer) == 0)
        assert(self.tdmaComm.commStartTime == None)

        # Send test packet
        packet = self.tdmaComm.packageMeshPacket(0, payloadBytes)
        self.tdmaComm.bufferTxMsg(packet)
        self.tdmaComm.sendBuffer()
        time.sleep(0.1)
        
        # Confirm payload bytes buffered for host
        assert(len(self.tdmaComm.hostBuffer) == 0)
        self.tdmaComm.readMsgs()
        self.tdmaComm.processMsgs()
        assert(len(self.tdmaComm.hostBuffer) > 0)
        assert(self.tdmaComm.hostBuffer == payloadBytes)

        # Confirm mesh admin message processed
        assert(self.tdmaComm.commStartTime == commStartTime)

        ## Test proper relaying
        cmdRelayBufferLen = len(self.tdmaComm.cmdRelayBuffer)
        assert(cmdRelayBufferLen > 0) # new broadcast command should be relayed
        self.tdmaComm.bufferTxMsg(packet)
        self.tdmaComm.sendBuffer()
        time.sleep(0.1)
        self.tdmaComm.readMsgs()
        self.tdmaComm.processMsgs()
        assert(len(self.tdmaComm.cmdRelayBuffer) == cmdRelayBufferLen) # stale command should be tossed
        # Send msg with destination that should be relayed
        self.tdmaComm.maxNumSlots = 7
        self.tdmaComm.meshPaths = [[]*7]*7
        self.tdmaComm.nodeParams.linkStatus = [[0, 1, 1, 0, 0, 0, 0],
                                               [1, 0, 0, 1, 0 ,0, 0],
                                               [1, 0, 0, 1, 0, 0, 0],
                                               [0, 1, 1, 0, 1, 1, 0],
                                               [0, 0, 0, 1, 0, 0, 1],
                                               [0, 0, 0, 1, 0, 0, 1],
                                               [0, 0, 0, 0, 1, 1, 0]]
        self.tdmaComm.updateShortestPaths()
        
        self.tdmaComm.cmdRelayBuffer = bytearray()
        destId = 3
        self.tdmaComm.nodeParams.config.nodeId = 2
        packet = self.tdmaComm.packageMeshPacket(destId, payloadBytes)
        self.tdmaComm.bufferTxMsg(packet)
        self.tdmaComm.sendBuffer()
        time.sleep(0.1)
        self.tdmaComm.readMsgs()
        self.tdmaComm.nodeParams.config.nodeId = 1
        self.tdmaComm.processMsgs()
        assert(len(self.tdmaComm.cmdRelayBuffer) > 0)

        # Send msg with destination that should not be relayed
        self.tdmaComm.cmdRelayBuffer = bytearray()
        destId = 4
        self.tdmaComm.nodeParams.config.nodeId = 2
        packet = self.tdmaComm.packageMeshPacket(destId, payloadBytes)
        self.tdmaComm.bufferTxMsg(packet)
        self.tdmaComm.sendBuffer()
        time.sleep(0.1)
        self.tdmaComm.readMsgs()
        self.tdmaComm.nodeParams.config.nodeId = 1
        self.tdmaComm.processMsgs()
        assert(len(self.tdmaComm.cmdRelayBuffer) == 0)
def processMsg(self, cmdId, msg, args):
        nodeStatus = args['nodeStatus'] 
        comm = args['comm']
        clock = args['clock']

        cmdStatus = False
 
        if len(msg) > 0:
            # Parse command header
            header = deserialize(msg, cmdId, 'header')
            if (processHeader(self, header, msg, nodeStatus, clock, comm) == False): # stale command
                return False

            # Parse message contents
            if cmdId not in [TDMACmds['BlockData'], TDMACmds['LinkStatus']]:
                try: 
                    msgContents = deserialize(msg, cmdId, 'body')       
                    if msgContents == None:
                        return False
                except Exception as e:
                    print("Exception occurred while deserializing message:", e)
                    return False
            
            # Process message by command id
            for case in switch(cmdId):
                if case(TDMACmds['TimeOffset']):
                    if nodeStatus and len(nodeStatus) >= header['sourceId']:
                        nodeStatus[header['sourceId']-1].timeOffset = msgContents['timeOffset']/100.0
                    cmdStatus = True
                    break
                    
                if case(TDMACmds['MeshStatus']):
                    if not comm.commStartTime: # accept value if one not already present
                        comm.commStartTime = msgContents['commStartTimeSec'] # store comm start time
                    cmdStatus = True

                    break

                if case(TDMACmds['LinkStatus']): # Status of links to other nodes
                    msgSize = self.nodeParams.config.maxNumNodes
                    headerSize = calcsize(headers[CmdDict[cmdId].header]['format'])
                    linkData = unpack('=' + msgSize*CmdDict['LinkStatusContents'].packFormat, msg[headerSize:])
       
                    # Determine sending node array index
                    node =  header['sourceId'] - 1

                    # Place received data into appropriate link status subarray
                    for i in range(msgSize):
                       self.nodeParams.linkStatus[node][i] = linkData[i]
                    cmdStatus = True
                    break

                if case(TDMACmds['BlockTxRequest']): # Request for a block of transmit time
                    blockTxResponse = False
                    if comm.blockTxStatus['status'] == TDMABlockTxStatus.false: # no current block tx active
                        # Check block transmit constraints
                        if validateBlockTxRequest(msgContents, header, self.nodeParams):        
                            # Store pending request parameters
                            comm.blockTxStatus['blockReqID'] = msgContents['blockReqID']
                            comm.blockTxStatus['status'] = TDMABlockTxStatus.pending # set to pending status and wait for start
                            comm.blockTxStatus['startTime'] = msgContents['startTime'] # store time of request
                            comm.blockTxStatus['txNode'] = header['sourceId'] # store requesting node's ID
                            comm.blockTxStatus['length'] = msgContents['length'] # transmit block length

                            blockTxResponse = True # set response to request

                    elif comm.blockTxStatus['status'] == TDMABlockTxStatus.pending:
                        if comm.blockTxStatus['blockReqID'] == msgContents['blockReqId'] and header['sourceId'] == comm.blockTxStatus['txNode']: # repeat positive response
                            blockTxResponse = True  
                            
                    # Send response
                    responseCmd = Command(TDMACmds['BlockTxRequestResponse'], {'blockReqID': msgContents['blockReqID'], 'accept': blockTxResponse}, [TDMACmds['BlockTxRequestResponse'], self.nodeParams.config.nodeId, self.nodeParams.get_cmdCounter()])
                    comm.radio.bufferTxMsg(responseCmd.serialize(self.nodeParams.clock.getTime()))
            
                    break           

                if case(TDMACmds['BlockTxConfirmed']): # Transmit block confirmation
                    if comm.blockTxStatus['status'] == TDMABlockTxStatus.pending: # pending block transmit
                        if comm.blockTxStatus['blockReqID'] == msgContents['blockReqID'] and comm.blockTxStatus['txNode'] == header['sourceId']: # confirmation received correct node with correct block ID
                            comm.blockTxStatus['status'] = TDMABlockTxStatus.confirmed              
                    break        

                if case(TDMACmds['BlockTxRequestResponse']): 
                    if comm.blockTxStatus['status'] == TDMABlockTxStatus.pending and comm.blockTxStatus['txNode'] == self.nodeParams.config.nodeId: # Block Tx previously requested by this node
                        if header['sourceId'] in comm.blockTxStatus['blockResponseList']: # this node in response list
                            comm.blockTxStatus['blockResponseList'][header['sourceId']] = msgContents['accept']
                    break

                if case(TDMACmds['BlockTxStatus']):
                    updateStatus = False
                    if comm.blockTxStatus['status'] == TDMABlockTxStatus.false: # receiving node not aware of block transmit    
                        updateStatus = True
                        
                    elif comm.blockTxStatus['status'] == TDMABlockTxStatus.pending:
                        if msgContents['blockReqID'] == comm.blockTxStatus['blockReqID']: # may have missed confirm message
                            updateStatus = True

                    if updateStatus:
                        # Update block transmit status
                        comm.blockTxStatus['blockReqID'] = msgContents['blockReqID']
                        comm.blockTxStatus['status'] = TDMABlockTxStatus.confirmed # jump to confirmed status
                        comm.blockTxStatus['startTime'] = msgContents['startTime'] # store time of request
                        comm.blockTxStatus['txNode'] = header['sourceId'] # store requesting node's ID
                        comm.blockTxStatus['length'] = msgContents['length'] # transmit block length

                    break

                if case(TDMACmds['BlockData']): # raw block data
                    headerSize = calcsize(headers[CmdDict[cmdId].header]['format'])
                    blockData = msg[headerSize:]
                    print("Block data received:", blockData)
                    
        return cmdStatus                    
예제 #10
0
def processMsg(self, cmdId, msg, args):
    nodeStatus = args['nodeStatus']
    comm = args['comm']
    clock = args['clock']

    if len(msg) > 0:
        # Parse command header
        header = deserialize(msg, cmdId, 'header')
        if header != None:
            updateNodeMsgRcvdStatus(nodeStatus, header, clock)
            # Check for command counter
            cmdStatus = checkCmdCounter(self, header, msg, comm)
            if cmdStatus == False:  # old command counter
                return

        # Parse message contents
        if cmdId not in [
                TDMACmds['TimeOffsetSummary'], TDMACmds['BlockData'],
                TDMACmds['LinkStatus'], TDMACmds['LinkStatusSummary']
        ]:
            try:
                msgContents = deserialize(msg, cmdId, 'body')
                if msgContents == None:
                    return
            except KeyError:
                print("Invalid command ID.")
                return

        # Process message by command id
        for case in switch(cmdId):
            if case(TDMACmds['TimeOffset']):
                if nodeStatus:
                    nodeStatus[
                        header['sourceId'] -
                        1].timeOffset = msgContents['timeOffset'] / 100.0
                break

            if case(TDMACmds['MeshStatus']):
                if self.nodeParams.commStartTime == []:  # accept value if one not already present
                    self.nodeParams.commStartTime = msgContents[
                        'commStartTimeSec']  # store comm start time

                break

            if case(TDMACmds['TimeOffsetSummary']
                    ):  # Compiled table of time offsets from all nodes
                # Calculate header size
                headerSize = calcsize(headers[CmdDict[cmdId].header]['format'])
                # Unpack message
                msgSize = calcsize(CmdDict[cmdId].packFormat)
                timeOffsetFormatSize = calcsize(
                    CmdDict['TimeOffsetSummaryContents'].packFormat)
                numNodes = unpack(CmdDict[cmdId].packFormat,
                                  msg[headerSize:headerSize + msgSize])[0]
                if (len(msg) !=
                    (numNodes * timeOffsetFormatSize) + headerSize + msgSize):
                    print("Serial message length incorrect length:",
                          str(len(msg)))
                    break

                for i in range(numNodes):  # Extract time offset of all nodes
                    msgContents = deserialize(
                        msg[i * timeOffsetFormatSize + headerSize +
                            msgSize:i * timeOffsetFormatSize +
                            (timeOffsetFormatSize + headerSize + msgSize)],
                        'TimeOffsetSummaryContents', 'body')
                    if msgContents == None:
                        return
                    nodeStatus[i].timeOffset = msgContents['offset'] / 100.0
                break

            if case(TDMACmds['LinkStatus']):  # Status of links to other nodes
                msgSize = self.nodeParams.config.maxNumNodes
                headerSize = calcsize(headers[CmdDict[cmdId].header]['format'])
                linkData = unpack(
                    '=' + msgSize * CmdDict['LinkStatusContents'].packFormat,
                    msg[headerSize:])

                # Determine sending node array index
                node = header['sourceId'] - 1

                # Place received data into appropriate link status subarray
                for i in range(msgSize):
                    self.nodeParams.linkStatus[node][i] = linkData[i]
                break

            if case(TDMACmds['LinkStatusSummary']
                    ):  # Complete table of links between nodes
                msgSize = self.nodeParams.config.maxNumNodes
                headerSize = calcsize(headers[CmdDict[cmdId].header]['format'])
                linkTable = unpack(
                    '=' + msgSize * msgSize *
                    CmdDict['LinkStatusSummaryContents'].packFormat,
                    msg[headerSize:])
                # Place received data into link status array
                for i in range(msgSize):
                    for j in range(msgSize):
                        self.nodeParams.linkStatus[i][j] = linkTable[i *
                                                                     msgSize +
                                                                     j]
                break
            if case(TDMACmds['BlockTxRequest']
                    ):  # Request for a block of transmit time
                blockTxResponse = False
                if comm.blockTxStatus[
                        'status'] == TDMABlockTxStatus.false:  # no current block tx active
                    # Check block transmit constraints
                    if validateBlockTxRequest(msgContents, header,
                                              self.nodeParams):
                        # Store pending request parameters
                        comm.blockTxStatus['blockReqID'] = msgContents[
                            'blockReqID']
                        comm.blockTxStatus[
                            'status'] = TDMABlockTxStatus.pending  # set to pending status and wait for start
                        comm.blockTxStatus['startTime'] = msgContents[
                            'startTime']  # store time of request
                        comm.blockTxStatus['txNode'] = header[
                            'sourceId']  # store requesting node's ID
                        comm.blockTxStatus['length'] = msgContents[
                            'length']  # transmit block length

                        blockTxResponse = True  # set response to request

                elif comm.blockTxStatus['status'] == TDMABlockTxStatus.pending:
                    if comm.blockTxStatus['blockReqID'] == msgContents[
                            'blockReqId'] and header[
                                'sourceId'] == comm.blockTxStatus[
                                    'txNode']:  # repeat positive response
                        blockTxResponse = True

                # Send response
                responseCmd = Command(
                    TDMACmds['BlockTxRequestResponse'], {
                        'blockReqID': msgContents['blockReqID'],
                        'accept': blockTxResponse
                    }, [
                        TDMACmds['BlockTxRequestResponse'],
                        self.nodeParams.config.nodeId,
                        self.nodeParams.get_cmdCounter()
                    ])
                comm.radio.bufferTxMsg(
                    responseCmd.serialize(self.nodeParams.clock.getTime()))

                break

            if case(TDMACmds['BlockTxConfirmed']
                    ):  # Transmit block confirmation
                if comm.blockTxStatus[
                        'status'] == TDMABlockTxStatus.pending:  # pending block transmit
                    if comm.blockTxStatus['blockReqID'] == msgContents[
                            'blockReqID'] and comm.blockTxStatus[
                                'txNode'] == header[
                                    'sourceId']:  # confirmation received correct node with correct block ID
                        comm.blockTxStatus[
                            'status'] = TDMABlockTxStatus.confirmed
                break

            if case(TDMACmds['BlockTxRequestResponse']):
                if comm.blockTxStatus[
                        'status'] == TDMABlockTxStatus.pending and comm.blockTxStatus[
                            'txNode'] == self.nodeParams.config.nodeId:  # Block Tx previously requested by this node
                    if header['sourceId'] in comm.blockTxStatus[
                            'blockResponseList']:  # this node in response list
                        comm.blockTxStatus['blockResponseList'][
                            header['sourceId']] = msgContents['accept']
                break

            if case(TDMACmds['BlockTxStatus']):
                updateStatus = False
                if comm.blockTxStatus[
                        'status'] == TDMABlockTxStatus.false:  # receiving node not aware of block transmit
                    updateStatus = True

                elif comm.blockTxStatus['status'] == TDMABlockTxStatus.pending:
                    if msgContents['blockReqID'] == comm.blockTxStatus[
                            'blockReqID']:  # may have missed confirm message
                        updateStatus = True

                if updateStatus:
                    # Update block transmit status
                    comm.blockTxStatus['blockReqID'] = msgContents[
                        'blockReqID']
                    comm.blockTxStatus[
                        'status'] = TDMABlockTxStatus.confirmed  # jump to confirmed status
                    comm.blockTxStatus['startTime'] = msgContents[
                        'startTime']  # store time of request
                    comm.blockTxStatus['txNode'] = header[
                        'sourceId']  # store requesting node's ID
                    comm.blockTxStatus['length'] = msgContents[
                        'length']  # transmit block length

                break

            if case(TDMACmds['BlockData']):  # raw block data
                headerSize = calcsize(headers[CmdDict[cmdId].header]['format'])
                blockData = msg[headerSize:]
                print("Block data received:", blockData)