unit NMRAnetStateMachine; // ****************************************************************************** // // * Copyright: // (c) Mustangpeak Software 2012. // // The contents of this file are subject to the GNU GPL v3 licence/ you maynot use // this file except in compliance with the License. You may obtain a copy of the // License at http://www.gnu.org/licenses/gpl.html //MF_DUPLICATE_ALIAS // * Revision History: // 2012-02-01: Created // 2012-10-07: Version 1.0 // // * Description: // Implements NMRAnet StateMachine for the main loop in a programg. // // ****************************************************************************** {$I Options.inc} uses {$IFDEF CAN_BUS} {$IFDEF dsPIC33} dsPIC33_CAN, {$ELSE} dsPIC30_CAN, {$ENDIF} CANStorage, {$ENDIF CAN_BUS} {$IFDEF ETHERNET_BUS} TCPStorage, {$ENDIF} {$IFDEF ETHERNET_BUS_MICROPASCAL_LIB} TCPStorage_mPascal_Lib, {$ENDIF} {$IFDEF TRACTION_PROTOCOL} TractionProtocol, {$ENDIF} NMRAnetAppDefines, NMRAnetDefines, NMRAnetDefinesShared, NMRAnetUtilities, NMRAnetCANReceive, NMRAnetNode, NMRAnetBufferPools; {.$DEFINE TRACE_DATAGRAM_SEND} {.$DEFINE TRACE_BUFFER_ADDRESSES} {.$DEFINE TRACE_MEM_CONFIG_STATEMACHINE} {.$DEFINE TRACE_DATAGRAM_REPLY} {.$DEFINE TRACE_CONFIG_MEM_DECODE} {.$DEFINE TRACE_NODE_STATEMACHINE} {.DEFINE TRACE_ACDI} {.$DEFINE TRACE_SNIP} {.$DEFINE TRACE_MEM_CONFIG_OP_STATEMACHINE} {.$DEFINE DISABLE_DATAGRAM_ABANDON_TIMEOUT} procedure NMRAnetStateMachine_Process(Node: PNMRAnetNode); procedure NMRAnetStateMachine_Disconnect(Node: PNMRAnetNode); procedure NMRAnetStateMachine_Initialize(PhysicalNodeID_HI, PhysicalNodeID_Lo: DWord); procedure NMRAnetStateMachine_InitializeNode(Node: PNMRAnetNode; NodeID_HI, NodeID_Lo: DWord); procedure NMRAnetStateMachine_100ms_Timer(Node: PNMRAnetNode); // Send Helper Functions, send via Statemachine loops function NMRAnetStateMachine_TrySendIdentifyProducer(Node: PNMRAnetNode; EventID: PEventID): Boolean; function NMRAnetStateMachine_TrySendIdentifyConsumer(Node: PNMRAnetNode; EventID: PEventID): Boolean; function NMRAnetStateMachine_TrySendIdentifyEvents(Node: PNMRAnetNode): Boolean; function NMRAnetStateMachine_TrySendAliasMapEnquiry(Node: PNMRAnetNode): Boolean; function NMRAnetStateMachine_TrySendAliasMapReset(Node: PNMRAnetNode): Boolean; function NMRAnetStateMachine_TrySendVerifyNodeID(Node: PNMRAnetNode; DestinationAliasID: Word): Boolean; function NMRAnetStateMachine_TrySendAbbreviatedCDI(Node: PNMRAnetNode; DestinationAliasID: Word): Boolean; function NMRAnetStateMachine_TrySendDatagram(Node: PNMRAnetNode; CANBuffer: PCANBuffer; Datagram: PDatagramBuffer): Boolean; function AppCallback_StateMachine(Node: PNMRAnetNode; CANBuffer: PCANBuffer; DataBytesPtr: PCAN_DataBytes): Boolean; external; function AppCallback_UserMessageFlags(Node: PNMRAnetNode; CANBuffer: PCANBuffer; DataBytesPtr: PCAN_DataBytes): Boolean; external; procedure NMRAnetAppCallbacks_Initialize; external; // Address Space Support // *************************************************************************** function AppCallback_AddressSpacePresent(Node: PNMRAnetNode; AddressSpace: Byte): Boolean; external; function AppCallback_AddressSpaceReadOnly(Node: PNMRAnetNode; AddressSpace: Byte): Boolean; external; function AppCallback_AddressSpaceSize(Node: PNMRAnetNode; AddressSpace: Byte): DWord; external; procedure AppCallback_Configuration_Read(Node: PNMRAnetNode; ConfigMemBuffer: PConfigMemBuffer); external; procedure AppCallback_Configuration_Write(Node: PNMRAnetNode; ConfigMemBuffer: PConfigMemBuffer); external; function AppCallback_AddressSpace_Read(Node: PNMRAnetNode; ConfigMemBuffer: PConfigMemBuffer): Boolean; external; function AppCallback_AddressSpace_Write(Node: PNMRAnetNode; ConfigMemBuffer: PConfigMemBuffer): Boolean; external; procedure AppCallback_ConfigMemReadWriteAckFlags(Node: PNMRAnetNode; ConfigMemBuffer: PConfigMemBuffer); external; procedure AppCallback_Configuration_CheckForComplete(Node: PNMRAnetNode; ConfigMemBuffer: PConfigMemBuffer); external; // Forward declarations procedure TransmitNMRABusLayerMsg(Node: PNMRAnetNode; Buffer: PCANBuffer; VariableField: DWord; DestinationAliasID: Word; DataCount: Byte; DataBytes: PCAN_DataBytes; AliasInHeader: Boolean; UpperNibbleFlagMask: Word); forward; procedure TransmitCANLayerMsg(Node: PNMRAnetNode; Buffer: PCANBuffer; VariableField: DWord); forward; function NMRABusTxBufferAvailable: Boolean; forward; function CANBusBufferAvailable: Boolean; forward; implementation // ***************************************************************************** // procedure NMRAnetStateMachine_TrySendAbbreviatedCDI // Parameters: // Returns: // // Description: // // ***************************************************************************** function NMRAnetStateMachine_TrySendAbbreviatedCDI(Node: PNMRAnetNode; DestinationAliasID: Word): Boolean; var CANBuffer: TCANBUffer; DataBytes: TCAN_DataBytes; begin Result := True; if NMRABusTxBufferAvailable then TransmitNMRABusLayerMsg(Node, @CANBuffer, MTI_SIMPLE_NODE_INFO_REQUEST, DestinationAliasID, 0, @DataBytes, False, $00) else Result := False end; // ***************************************************************************** // procedure NMRAnetStateMachine_TrySendIdentifyProducer // Parameters: // Returns: // // Description: // // ***************************************************************************** function NMRAnetStateMachine_TrySendIdentifyProducer(Node: PNMRAnetNode; EventID: PEventID): Boolean; var CANBuffer: TCANBUffer; begin Result := True; if NMRABusTxBufferAvailable then TransmitNMRABusLayerMsg(Node, @CANBuffer, MTI_PRODUCER_IDENDIFY, 0, EVENT_ARRAY_LENGTH, PCAN_DataBytes( EventID), False, $00) else Result := False end; // ***************************************************************************** // procedure NMRAnetStateMachine_TrySendIdentifyConsumer // Parameters: // Returns: // // Description: // // ***************************************************************************** function NMRAnetStateMachine_TrySendIdentifyConsumer(Node: PNMRAnetNode; EventID: PEventID): Boolean; var CANBuffer: TCANBUffer; begin Result := True; if NMRABusTxBufferAvailable then TransmitNMRABusLayerMsg(Node, @CANBuffer, MTI_CONSUMER_IDENTIFY, 0, EVENT_ARRAY_LENGTH, PCAN_DataBytes( EventID), False, $00) else Result := False end; // ***************************************************************************** // procedure NMRAnetStateMachine_TrySendIdentifyEvents // Parameters: // Returns: // // Description: // // ***************************************************************************** function NMRAnetStateMachine_TrySendIdentifyEvents(Node: PNMRAnetNode): Boolean; var CANBuffer: TCANBUffer; begin Result := True; if NMRABusTxBufferAvailable then TransmitNMRABusLayerMsg(Node, @CANBuffer, MTI_EVENTS_IDENTIFY, 0, 0, nil, False, $00) else Result := False; end; // ***************************************************************************** // procedure NMRAnetStateMachine_TrySendAliasMapEnquiry // Parameters: // Returns: // // Description: // // ***************************************************************************** function NMRAnetStateMachine_TrySendAliasMapEnquiry(Node: PNMRAnetNode): Boolean; var CANBuffer: TCANBUffer; begin Result := True; if CANBusBufferAvailable then TransmitCANLayerMsg(Node, @CANBuffer, MTI_AME) else Result := False; end; // ***************************************************************************** // procedure NMRAnetStateMachine_TrySendAliasMapReset // Parameters: // Returns: // // Description: // // ***************************************************************************** function NMRAnetStateMachine_TrySendAliasMapReset(Node: PNMRAnetNode): Boolean; var CANBuffer: TCANBUffer; begin Result := True; if CANBusBufferAvailable then TransmitCANLayerMsg(Node, @CANBuffer, MTI_AMR) else Result := False; end; // ***************************************************************************** // procedure NMRAnetStateMachine_TrySendVerifyNodeID // Parameters: // Returns: // // Description: // // ***************************************************************************** function NMRAnetStateMachine_TrySendVerifyNodeID(Node: PNMRAnetNode; DestinationAliasID: Word): Boolean; var DataBytes: TCAN_DataBytes; CANBuffer: TCANBUffer; begin Result := False; if NMRABusTxBufferAvailable then begin if DestinationAliasID <> 0 then TransmitNMRABusLayerMsg(Node, @CANBuffer, MTI_VERIFY_NODE_ID_NUMBER_DEST, DestinationAliasID, 0, @DataBytes, False, $00) else TransmitNMRABusLayerMsg(Node, @CANBuffer, MTI_VERIFY_NODE_ID_NUMBER, 0, 0, nil, False, $00); end else Result := False end; // ***************************************************************************** // procedure NMRAnetStateMachine_TrySendDatagram // Parameters: // Returns: // // Description: Returns True when the Datagram is COMPLETLY sent // Expects the iDataCount, DataBytes, Alias (destination) to be valid and Tag to be // initaialized to 0 // // ***************************************************************************** function NMRAnetStateMachine_TrySendDatagram(Node: PNMRAnetNode; CANBuffer: PCANBuffer; Datagram: PDatagramBuffer): Boolean; var i: Integer; DataBytes: TCAN_DataBytes; MTI: DWord; begin Result := False; if NMRABusTxBufferAvailable then begin {$IFDEF TRACE_DATAGRAM_SEND}IntToStr(Datagram^.iByteCount, s1); UART1_Write_Text('ByteCount = '+s1+LF);{$ENDIF} {$IFDEF TRACE_DATAGRAM_SEND}IntToStr(Datagram^.Tag, s1); UART1_Write_Text('Tag = '+s1+LF);{$ENDIF} if Datagram^.iByteCount <= 8 then // Single Frame Datagram begin {$IFDEF TRACE_DATAGRAM_SEND}UART1_Write_Text('MTI_FRAME_TYPE_DATAGRAM_ONLY_FRAME'+LF);{$ENDIF} for i := 0 to Datagram^.iByteCount - 1 do DataBytes[i] := Datagram^.DataBytes[i]; TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_FRAME_TYPE_DATAGRAM_ONLY_FRAME, Datagram^.Alias, Datagram^.iByteCount, @DataBytes, True, $00); Result := True; end else begin // Calculate which frame it is for the Multi Frame Datagram if Datagram^.Tag = 0 then begin MTI := MTI_FRAME_TYPE_DATAGRAM_FRAME_START {$IFDEF TRACE_DATAGRAM_SEND};UART1_Write_Text('MTI_DATAGRAM_FRAME_START_SEND'+LF);{$ENDIF} end else if Datagram^.iByteCount - Datagram^.Tag > 8 then begin MTI := MTI_FRAME_TYPE_DATAGRAM_FRAME {$IFDEF TRACE_DATAGRAM_SEND};UART1_Write_Text('MTI_DATAGRAM_FRAME_SEND'+LF);{$ENDIF} end else begin MTI := MTI_FRAME_TYPE_DATAGRAM_FRAME_END; {$IFDEF TRACE_DATAGRAM_SEND}UART1_Write_Text('MTI_DATAGRAM_FRAME_END_SEND'+LF);{$ENDIF} Result := True end; i := 0; while (Datagram^.Tag < Datagram^.iByteCount) and (i < 8) do // Copy 8 Data Byte, or as many that are left to the buffer begin DataBytes[i] := Datagram^.DataBytes[Datagram^.Tag]; Inc(i); Inc(Datagram^.Tag); end; {$IFDEF TRACE_DATAGRAM_SEND}UART1_Write_Text('TransmitNMRABusLayerMsg = Transmitting'+LF);{$ENDIF} TransmitNMRABusLayerMsg(Node, CANBuffer, MTI, Datagram^.Alias, i, @DataBytes, True, $00); {$IFDEF TRACE_DATAGRAM_SEND}UART1_Write_Text('TransmitNMRABusLayerMsg = Transmitted'+LF);{$ENDIF} end; end end; // ***************************************************************************** // procedure NMRAnetStateMachine_TrySendStreamInitiate // Parameters: // Returns: // // Description: Returns True when the Datagram is COMPLETLY sent // Expects the iDataCount, DataBytes, Alias (destination) to be valid and Tag to be // initaialized to 0 // // ***************************************************************************** function NMRAnetStateMachine_TrySendStreamInitiate(Node: PNMRAnetNode; CANBuffer: PCANBuffer; Stream: PStreamBuffer): Boolean; var DataBytes: TCAN_DataBytes; MTI: DWord; DestAliasFlagMask: Byte; begin Result := False; DestAliasFlagMask := 0; if NMRABusTxBufferAvailable then begin if Stream^.NegotiatedTransferSize > MAX_STREAM_LEN then Stream^.NegotiatedTransferSize := MAX_STREAM_LEN; DataBytes[0] := Stream^.NegotiatedTransferSize shr 24; DataBytes[1] := Stream^.NegotiatedTransferSize shr 16; DataBytes[2] := Stream^.NegotiatedTransferSize shr 8; DataBytes[3] := Stream^.NegotiatedTransferSize shr 0; DataBytes[4] := Stream^.LocalStreamID; if Stream^.Content.TypeIncluded then DestAliasFlagMask := DestAliasFlagMask or $10; TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_FRAME_TYPE_STREAM_INIT_REQUEST, Stream^.Alias, 5, @DataBytes, False, DestAliasFlagMask); Result := True end end; // ***************************************************************************** // procedure NMRAnetStateMachine_TrySendStreamInitiateReply // Parameters: // Returns: // // Description: Returns True when the Datagram is COMPLETLY sent // Expects the iDataCount, DataBytes, Alias (destination) to be valid and Tag to be // initaialized to 0 // // ***************************************************************************** function NMRAnetStateMachine_TrySendStreamInitiateReply(Node: PNMRAnetNode; CANBuffer: PCANBuffer; Stream: PStreamBuffer): Boolean; var DataBytes: TCAN_DataBytes; MTI: DWord; begin Result := False; if NMRABusTxBufferAvailable then begin if Stream^.NegotiatedTransferSize > MAX_STREAM_LEN then Stream^.NegotiatedTransferSize := MAX_STREAM_LEN; DataBytes[0] := Stream^.NegotiatedTransferSize shr 24; DataBytes[1] := Stream^.NegotiatedTransferSize shr 16; DataBytes[2] := Stream^.NegotiatedTransferSize shr 8; DataBytes[3] := Stream^.NegotiatedTransferSize shr 0; DataBytes[4] := Stream^.LocalStreamID; TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_FRAME_TYPE_STREAM_INIT_REPLY, Stream^.Alias, 5, @DataBytes, False, $00); Result := True end end; // ***************************************************************************** // procedure NMRAnetStateMachine_InitializeNode // Parameters: // Returns: // // Description: // // ***************************************************************************** procedure NMRAnetStateMachine_InitializeNode(Node: PNMRAnetNode; NodeID_HI, NodeID_LO: DWord); var i: Integer; begin // Don't Reset the Addresses here, they are initialized on bootup and are fixed thoughout the life of the program Node^.BaseBuffers := nil; Node^.DatagramBuffers := nil; Node^.ConfigMemBuffers := nil; Node^.DataBuffers := nil; Node^.State := 0; Node^.MsgFlags := 0; Node^.MsgFlagsUserDefined := 0; Node^.ParentAlias := 0; Node^.ChildAlias := 0; if (NodeID_HI <> 0) and (NodeID_LO <> 0) then begin // Only done on the very first initialization Node^.Info.ID[0] := NodeID_LO; Node^.Info.ID[1] := NodeID_HI; Node^.Info.Seed := Node^.Info.ID; Node^.Login.TimeCounter := 0; Node^.Login.iCID := 0; end; for i := 0 to MAX_EVENTS_CONSUMED_BIT_BYTES - 1 do Node^.EventsConsumedFlags[i] := 0; for i := 0 to MAX_EVENTS_PRODUCED_BIT_BYTES - 1 do Node^.EventsProducedFlags[i] := 0; for i := 0 to MAX_PCER_BIT_BYTES - 1 do Node^.PCER_Flags[i] := 0; Node^.iStateMachine := STATE_NMRABUS_START; Node^.ParentAlias := nil; Node^.ChildAlias := nil; Node^.LeftSibling := nil; Node^.RightSibling := nil; Node^.Login.TimeCounter := 0; Node^.Login.iCID := 0; {$IFDEF TRACTION_PROTOCOL} NMRAnetTractionProtocol_InitializeRamData( NMRAnetTractionProtocol_ExtractTrainProxyRamData(Node)); {$ENDIF} // I don't think I want to reset these as they are set at startup // ConfigurationAddress: Generic32BitPointer; // Pointer into the EEProm Memory // RAMAddress: Generic32BitPointer; // Pointer to a DataStructure that is in RAM end; // ***************************************************************************** // procedure NMRAnetStateMachine_Initialize // Parameters: // Returns: // // Description: // // ***************************************************************************** procedure NMRAnetStateMachine_Initialize(PhysicalNodeID_HI, PhysicalNodeID_Lo: DWord); begin {$IFDEF dsPIC33} {$IFDEF CAN_BUS} dsPIC33_CAN_Initialize; {$ENDIF} {$ENDIF} {$IFDEF TRACTION_PROTOCOL}NMRAnetTractionProtocol_Initialize;{$ENDIF} CANStorage_Initialize; NMRAnetCANReceive_Initialize; NMRAnetBufferPools_Initialize; NMRAnetAppCallbacks_Initialize; NMRAnetNode_Initialize(PhysicalNodeID_HI, PhysicalNodeID_Lo); end; // ***************************************************************************** // procedure NMRAnetStateMachine_100ms_Timer; // Parameters: // Returns: // // Description: // // ***************************************************************************** procedure NMRAnetStateMachine_100ms_Timer(Node: PNMRAnetNode); begin Inc(Node^.Login.TimeCounter); end; // ***************************************************************************** // procedure TransmitCANLayerMsg // Parameters: // Returns: // // Description: // // ***************************************************************************** procedure TransmitCANLayerMsg(Node: PNMRAnetNode; Buffer: PCANBuffer; VariableField: DWord); begin if CAN_Engine.TX_CANBuffer.State and BS_ALLOCATED = 0 then begin NMRAnetUtilities_CreateCANControlFrameCANBuffer(Node, Buffer, VariableField); CAN_Engine.TX_CANBuffer := Buffer^; CAN_Engine.TX_CANBuffer.State := CAN_Engine.TX_CANBuffer.State or BS_ALLOCATED; StartCANMessageEngine(); end else UART_Write_Text('ERROR: Can''t Transmit CAN Layer'+LF) end; // ***************************************************************************** // procedure CANBusBufferAvailable // Parameters: // Returns: // // Description: // // ***************************************************************************** function CANBusBufferAvailable: Boolean; begin Result := CAN_Engine.TX_CANBuffer.State and BS_ALLOCATED = 0 end; // ***************************************************************************** // procedure TransmitNMRABusLayerMsg // Parameters: // Returns: // // Description: This assumes the caller has tested for room in the buffer // // ***************************************************************************** procedure TransmitNMRABusLayerMsg(Node: PNMRAnetNode; Buffer: PCANBuffer; MTI: DWord; DestinationAlias: Word; DataCount: Byte; DataBytes: PCAN_DataBytes; AliasInHeader: Boolean; UpperNibbleFlagMask: Word); begin // TODO: IF THE DESTINATION IS A VNNODE THEN WE NEED TO NOT PUT IT IN THE CAN BUFFER BUT JUST SHORT CUT IT TO THE RECEIVE BUFFER if CANStorage_NMRAnetBufferAvailable then begin if AliasInHeader then NMRAnetUtilities_CreateNMRABusMessageCANBufferWithDestInMTI(Node, Buffer, MTI, DestinationAlias, DataCount, DataBytes) else NMRAnetUtilities_CreateNMRABusMessageCANBuffer(Node, Buffer, MTI, DestinationAlias, DataCount, DataBytes, UpperNibbleFlagMask); CANStorage_NMRAnetBufferStore(Buffer); StartCANMessageEngine(); {$IFDEF ETHERNET_BUS} CAN_Engine.TransmitImmediately := MTI = MTI_DATAGRAM_OK_REPLY; // Turn around Datagram ACK fast {$ENDIF} end else UART_Write_Text('ERROR: Can''t Transmit OLCB Layer'+LF); end; // ***************************************************************************** // procedure NMRABusTxBufferAvailable // Parameters: // Returns: // // Description: // // ***************************************************************************** function NMRABusTxBufferAvailable: Boolean; begin Result := CANStorage_NMRAnetBufferAvailable; end; // ***************************************************************************** // procedure MaxAddressByAddressSpace // Parameters: // Returns: // // Description: // // ***************************************************************************** {$IFNDEF BOOTLOADER} function MaxAddressByAddressSpace(Node: PNMRAnetNode; AddressSpace: Byte): DWord; begin case AddressSpace of MSI_CDI : begin {$IFDEF SUPPORT_VIRTUAL_NODES} if Node^.State and NS_VIRTUAL <> 0 then Result := MAX_CDI_ARRAY_VNODE else {$ENDIF} Result := MAX_CDI_ARRAY; end; MSI_ALL : Result := ALL_MAP.HighMem; MSI_ACDI_MFG : begin {$IFDEF SUPPORT_VIRTUAL_NODES} if Node^.State and NS_VIRTUAL <> 0 then Result := MAX_ACDI_MFG_ARRAY_VNODE + 1 // for the Version ID Byte else {$ENDIF} Result := MAX_ACDI_MFG_ARRAY + 1 // for the Version ID Byte end; MSI_ACDI_USER : begin Result := MAX_USER_CONFIG_DATA + 1 // for the Version ID Byte end; MSI_CONFIG, MSI_FSI, MSI_FDI : begin Result := AppCallback_AddressSpaceSize(Node, AddressSpace); end else Result := 0; end; end; {$ENDIF} // ***************************************************************************** // procedure DecodeConfigMemReadWriteHeader // Parameters: // Returns: // // Description: // // ***************************************************************************** function DecodeConfigMemReadWriteHeader(Node: PNMRAnetNode; DatagramBuffer: PDatagramBuffer; ConfigMemBuffer: PConfigMemBuffer): Boolean; var DataOffset: Byte; i: Integer; MaxSpaceSize: DWord; begin Result := True; // Set the Alias of the requesting node ConfigMemBuffer^.Alias := DatagramBuffer^.Alias; // Decode the Memory Space and where the Data starts DataOffset := 6; case DatagramBuffer^.DataBytes[1] and $03 of // Strip off bottom two bits MCP_CDI : ConfigMemBuffer^.AddressSpace := MSI_CDI; MCP_ALL : ConfigMemBuffer^.AddressSpace := MSI_ALL; MCP_CONFIGURATION : ConfigMemBuffer^.AddressSpace := MSI_CONFIG; MCP_NONE : begin Inc(DataOffset); ConfigMemBuffer^.AddressSpace := DatagramBuffer^.DataBytes[6] end; end; ConfigMemBuffer^.Address := DWord( DatagramBuffer^.DataBytes[2] shl 24) or DWord( DatagramBuffer^.DataBytes[3] shl 16) or DWord( DatagramBuffer^.DataBytes[4] shl 8) or DWord( DatagramBuffer^.DataBytes[5]); case DatagramBuffer^.DataBytes[1] and MCP_COMMAND_MASK of MCP_COMMAND_READ : begin ConfigMemBuffer^.Action := CBA_READ; ConfigMemBuffer^.DataCount := DatagramBuffer^.DataBytes[DataOffset] and $7F; // If it is a read then the value is passed by the caller; Must ignore the uppder bit per the spec; the DataBytes are already zeroed when the ConfigMemBuffer was allocated end; MCP_COMMAND_WRITE : begin ConfigMemBuffer^.Action := CBA_WRITE; ConfigMemBuffer^.DataCount := DatagramBuffer^.iByteCount - DataOffset; // If it is a write then copy the Data over to the ConfigMemBuffer for i := 0 to ConfigMemBuffer^.DataCount - 1 do ConfigMemBuffer^.DataBytes[i] := DatagramBuffer^.DataBytes[i + DataOffset]; end; MCP_COMMAND_READ_STREAM : begin ConfigMemBuffer^.Action := CBA_READ_STREAM; if NMRAnetBufferPools_AllocateStreamBuffer(ConfigMemBuffer^.Stream, True) then begin NMRAnetUtilities_StreamBufferLink(Node, ConfigMemBuffer^.Stream); // Link to the Node so the Stream StateMachine will run ConfigMemBuffer^.Stream^.TotalTransferCount := DWord((DatagramBuffer^.DataBytes[DataOffset] shl 24)) or DWord((DatagramBuffer^.DataBytes[DataOffset+1] shl 16)) or DWord((DatagramBuffer^.DataBytes[DataOffset+2] shl 8)) or DWord(DatagramBuffer^.DataBytes[DataOffset+3]); ConfigMemBuffer^.Stream^.LocalStreamID := DatagramBuffer^.DataBytes[DataOffset+4]; // Requester sent us a SID to use so it can reconize it end else Result := False end; MCP_COMMAND_WRITE_STREAM : begin if NMRAnetBufferPools_AllocateStreamBuffer(ConfigMemBuffer^.Stream, False) then begin ConfigMemBuffer^.Stream^.TotalTransferCount := DWord((DatagramBuffer^.DataBytes[DataOffset] shl 24)) or DWord((DatagramBuffer^.DataBytes[DataOffset+1] shl 16)) or DWord((DatagramBuffer^.DataBytes[DataOffset+2] shl 8)) or DWord(DatagramBuffer^.DataBytes[DataOffset+3]); ConfigMemBuffer^.Stream^.LocalStreamID := DatagramBuffer^.DataBytes[DataOffset+4]; // Requester sent us a SID to use so it can reconize it end else Result := False end; MCP_COMMAND_WRITE_UNDER_MASK : begin ConfigMemBuffer^.Action := CBA_WRITE_UNDER_MASK end; end; // Test the size against the size of the Address Space and adjust to the Max size if necessary MaxSpaceSize := MaxAddressByAddressSpace(Node, ConfigMemBuffer^.AddressSpace); if ConfigMemBuffer^.Address >= MaxSpaceSize then // If the caller overruns the address we are done ConfigMemBuffer^.DataCount := 0 else begin if ConfigMemBuffer^.Address + ConfigMemBuffer^.DataCount > MaxSpaceSize then ConfigMemBuffer^.DataCount := MaxSpaceSize - ConfigMemBuffer^.Address; end; end; // ***************************************************************************** // procedure EncodeConfigMemReadWriteHeader // Parameters: // Returns: // // Description: // // ***************************************************************************** procedure EncodeConfigMemReadWriteHeader(Node: PNMRAnetNode; DatagramBuffer: PDatagramBuffer; ConfigMemBuffer: PConfigMemBuffer); var DataOffset: Byte; i: Integer; begin DatagramBuffer^.DataBytes[0] := DATAGRAM_TYPE_MEMORY_CONFIGURATION; DataOffset := 6; case ConfigMemBuffer^.AddressSpace of // Strip off bottom two bits MSI_CDI : DatagramBuffer^.DataBytes[1] := MCP_CDI; MSI_ALL : DatagramBuffer^.DataBytes[1] := MCP_ALL; MSI_CONFIG : DatagramBuffer^.DataBytes[1] := MCP_CONFIGURATION else begin Inc(DataOffset); DatagramBuffer^.DataBytes[6] := ConfigMemBuffer^.AddressSpace end; end; DatagramBuffer^.DataBytes[2] := (DWord(ConfigMemBuffer^.Address) shr 24); DatagramBuffer^.DataBytes[3] := (DWord(ConfigMemBuffer^.Address) shr 16); DatagramBuffer^.DataBytes[4] := (DWord(ConfigMemBuffer^.Address) shr 8); DatagramBuffer^.DataBytes[5] := DWord(ConfigMemBuffer^.Address); if ConfigMemBuffer^.ErrorCode[0] > 0 then begin // Error found need to report it DatagramBuffer^.DataBytes[DataOffset] := ConfigMemBuffer^.ErrorCode[0]; DatagramBuffer^.DataBytes[DataOffset+1] := ConfigMemBuffer^.ErrorCode[1]; DatagramBuffer^.iByteCount := DataOffset + 2; if ConfigMemBuffer^.ErrorString^[0] <> #0 then begin DataOffset := DataOffset + 2; i := 0; while i < MAX_CONFIG_MEM_ERROR_STR_LEN do begin DatagramBuffer^.DataBytes[DataOffset + i] := ConfigMemBuffer^.ErrorString^[i]; if ConfigMemBuffer^.ErrorString^[i] = #0 then begin DatagramBuffer^.iByteCount := DataOffset + i + 1; Break; end; if i = MAX_CONFIG_MEM_ERROR_STR_LEN - 1 then begin if DatagramBuffer^.DataBytes[DataOffset + i] <> #0 then DatagramBuffer^.DataBytes[DataOffset + i] := #0; // If we don't find a null then we have to force one at the end end; Inc(i); end; end end; case ConfigMemBuffer^.Action of CBA_READ, CBA_READ_STREAM : begin if ConfigMemBuffer^.ErrorCode[0] > 0 then DatagramBuffer^.DataBytes[1] := DatagramBuffer^.DataBytes[1] or MCP_COMMAND_READ_REPLY_FAIL else begin DatagramBuffer^.DataBytes[1] := DatagramBuffer^.DataBytes[1] or MCP_COMMAND_READ_REPLY_OK; if ConfigMemBuffer^.Action = CBA_READ then // Stream information is filled in to its buffer begin for i := 0 to ConfigMemBuffer^.DataCount - 1 do DatagramBuffer^.DataBytes[i+DataOffset] := ConfigMemBuffer^.DataBytes[i]; DatagramBuffer^.iByteCount := ConfigMemBuffer^.DataCount + DataOffset; end end; end; CBA_WRITE, CBA_WRITE_STREAM : begin if ConfigMemBuffer^.ErrorCode[0] > 0 then DatagramBuffer^.DataBytes[1] := DatagramBuffer^.DataBytes[1] or MCP_COMMAND_WRITE_REPLY_FAIL else begin DatagramBuffer^.DataBytes[1] := DatagramBuffer^.DataBytes[1] or MCP_COMMAND_WRITE_REPLY_OK; if ConfigMemBuffer^.Action = CBA_READ then // Stream information is filled in to its buffer begin DatagramBuffer^.iByteCount := DataOffset; end end end; end; DatagramBuffer^.Alias := ConfigMemBuffer^.Alias; end; // ***************************************************************************** // procedure ProcessNode // Parameters: // Returns: // // Description: Expects CAN Disabled // // ***************************************************************************** procedure ProcessNode(Node: PNMRAnetNode; CANBuffer: PCANBuffer); const ADDRESSED = True; NONADDRESSED = False; CRITICAL_MSG_MASK = MF_DUPLICATE_NODE_ID or MF_DUPLICATE_ALIAS or MF_DUPLICATE_ALIAS_RID; type TByteArray = array[0..0] of Byte; // Byte array of any length PByteArray = ^TByteArray; var DataBytes: TCAN_DataBytes; DataBytesPtr: PCAN_DataBytes; i, j, EventIndex: Integer; BaseBuffer: PBaseBuffer; DatagramBuffer: PDatagramBuffer; StreamBuffer: PStreamBuffer; ConfigMemBuffer: PConfigMemBuffer; LocalConfigMemBuffer: TConfigMemBuffer; DataBuffer: PDataBuffer; State: Byte; MemorySpaceAddress, MemorySpaceMaxAddress: DWord; ByteArray: PByteArray; FailDatagram: Boolean; begin // The first thing to check is for duplicate Node ID's or Aliases and handle // them first and exclusively if Node^.MsgFlags <> 0 then begin if Node^.MsgFlags and CRITICAL_MSG_MASK <> 0 then // Subdivide to Critical vs. Noncritical messages begin // Critial Fault Messages if NMRAnetNode_TestMsgFlags(Node, MF_DUPLICATE_NODE_ID, True) then // Jump Statemachine here Node^.iStateMachine := STATE_NMRABUS_DUPLICATE_FULL_ID else if NMRAnetNode_TestMsgFlags(Node, MF_DUPLICATE_ALIAS, True) then // Jump Statemachine here Node^.iStateMachine := STATE_NMRABUS_INHIBITED else if NMRAnetNode_TestMsgFlags(Node, MF_DUPLICATE_ALIAS_RID, False) then // Tell the node that is our Alias begin if CANBusBufferAvailable then begin TransmitCANLayerMsg(Node, CANBuffer, MTI_RID); NMRAnetNode_ClearMsgFlag(Node, MF_DUPLICATE_ALIAS_RID); end end; Exit; // Important things happened and were handled, exit end; // Non Critical Fault Messages that require a response if NMRAnetNode_TestMsgFlags(Node, MF_ALIAS_MAP_ENQUIRY, False) then begin if CANBusBufferAvailable then begin TransmitCANLayerMsg(Node, CANBuffer, MTI_AMD); NMRAnetNode_ClearMsgFlag(Node, MF_ALIAS_MAP_ENQUIRY); end end else if NMRAnetNode_TestMsgFlags(Node, MF_VERIFY_NODE_ID, False) then begin // UART1_Write_Text('MF_VERIFY_NODE_ID'+LF); if NMRABusTxBufferAvailable then begin // UART1_Write_Text('MF_VERIFY_NODE_ID /w Buffer'+LF); NMRAnetUtilities_LoadFrameCANBufferDataWith48BitNodeID(Node, CANBuffer); TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_VERIFIED_NODE_ID_NUMBER, 0, 6, @CANBuffer^.DataBytes, False, $00); NMRAnetNode_ClearMsgFlag(Node, MF_VERIFY_NODE_ID); end; end; Exit; // Don't interleave Buffer Replies... end else if Node^.MsgFlagsUserDefined <> 0 then begin AppCallback_UserMessageFlags(Node, CANBuffer, @DataBytes); end else begin // Callback gets first crack... for now if AppCallback_StateMachine(Node, CANBuffer, @DataBytes) then begin Exit end; BaseBuffer := NMRAnetUtilities_NextBaseBuffer(Node); if BaseBuffer <> nil then begin {$IFDEF TRACE_BUFFER_ADDRESSES}UART1_Write_Text('BaseBuffer Statemachine :'); WordToHex(BaseBuffer, s1); UART1_Write_Text('$'+s1+LF); {$ENDIF} case BaseBuffer^.mCode of BMC_PROTOCOL_SUPPORT_QUERY : begin if NMRABusTxBufferAvailable then begin NMRAnetUtilities_ZeroCANData(DataBytes); {$IFDEF SUPPORT_VIRTUAL_NODES} if NMRAnetNode_TestStateFlag(NOde, NS_VIRTUAL) then begin for i := 0 to LEN_PIV_PROTOCOL-1 do for j := 0 to PIV_VNODE_SUPPORTED_PROTOCOL_COUNT - 1 do DataBytes[i] := DataBytes[i] or PIV_VNODE_SUPPORTED_PROTOCOLS[j][i]; end else {$ENDIF} begin for i := 0 to LEN_PIV_PROTOCOL-1 do for j := 0 to PIV_SUPPORTED_PROTOCOL_COUNT - 1 do DataBytes[i] := DataBytes[i] or PIV_SUPPORTED_PROTOCOLS[j][i]; end; TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_PROTOCOL_SUPPORT_REPLY, BaseBuffer^.Alias, 6, @DataBytes, False, $00); NMRAnetUtilities_BaseBufferUnLink(Node, BaseBuffer); NMRAnetBufferPools_ReleaseBaseBuffer(BaseBuffer); end; end; {$IFNDEF BOOTLOADER} BMC_SIMPLE_NODE_INFO_REQEUST : begin ConfigMemBuffer := nil; if NMRABusTxBufferAvailable then if NMRAnetBufferPools_AllocateConfigMemBuffer(ConfigMemBuffer) then begin i := 0; while (BaseBuffer^.StateMachine <> STATE_ACDI_DONE) and (i < 6) do // All messages have the Destination Alias as the first 2 bytes so only 6 left to use begin case BaseBuffer^.StateMachine of STATE_ACDI_MFG_VERSION : begin {$IFDEF TRACE_SNIP}UART1_Write_Text('STATE_ACDI_MFG_VERSION'+LF); {$ENDIF} DataBytes[i] := ACDI_MFG_VERSION; Inc(i); BaseBuffer^.Tag := 0; BaseBuffer^.StateMachine := STATE_ACDI_MFG_INFO; end; STATE_ACDI_MFG_INFO : begin {$IFDEF TRACE_SNIP}UART1_Write_Text('STATE_ACDI_MFG_INFO'+LF); {$ENDIF} {$IFDEF SUPPORT_VIRTUAL_NODES} if Node^.State and NS_VIRTUAL <> 0 then begin if BaseBuffer^.Tag < MAX_ACDI_MFG_ARRAY_VNODE then begin DataBytes[i] := ACDI_MFG_STRINGS_VNODE[BaseBuffer^.Tag]; Inc(BaseBuffer^.Tag); Inc(i); end else BaseBuffer^.StateMachine := STATE_ACDI_USER_VERSION; end else {$ENDIF} begin if BaseBuffer^.Tag < MAX_ACDI_MFG_ARRAY then begin DataBytes[i] := ACDI_MFG_STRINGS[BaseBuffer^.Tag]; Inc(BaseBuffer^.Tag); Inc(i); end else BaseBuffer^.StateMachine := STATE_ACDI_USER_VERSION; end; end; STATE_ACDI_USER_VERSION : begin {$IFDEF TRACE_SNIP}UART1_Write_Text('STATE_ACDI_USER_VERSION'+LF); {$ENDIF} DataBytes[i] := ACDI_USER_VERSION; Inc(i); BaseBuffer^.StateMachine := STATE_ACDI_USER_NAME; BaseBuffer^.Tag := 1; // EEPROM layout start at offset 1 end; STATE_ACDI_USER_NAME : begin {$IFDEF TRACE_SNIP}UART1_Write_Text('STATE_ACDI_USER_NAME'+LF); {$ENDIF} if BaseBuffer^.Tag < MAX_USER_NAME then begin // Very wasteful and slow 1 at a time but it is easy ConfigMemBuffer^.Address := BaseBuffer^.Tag; ConfigMemBuffer^.DataCount := 1; ConfigMemBuffer^.AddressSpace := MSI_CONFIG; AppCallback_Configuration_Read(Node, ConfigMemBuffer); DataBytes[i] := ConfigMemBuffer^.DataBytes[0]; if DataBytes[i] = #0 then BaseBuffer^.StateMachine := STATE_ACDI_START_DESC else if BaseBuffer^.Tag = MAX_USER_NAME - 1 then DataBytes[i] := #0; Inc(i); Inc(BaseBuffer^.Tag); end else BaseBuffer^.StateMachine := STATE_ACDI_START_DESC; end; STATE_ACDI_START_DESC : begin {$IFDEF TRACE_SNIP}UART1_Write_Text('STATE_ACDI_START_DESC'+LF); {$ENDIF} BaseBuffer^.Tag := MAX_USER_NAME + 1; // EEPROM layout start at offset 1 BaseBuffer^.StateMachine := STATE_ACDI_USER_DESC; end; STATE_ACDI_USER_DESC : begin {$IFDEF TRACE_SNIP}UART1_Write_Text('STATE_ACDI_USER_DESC'+LF); {$ENDIF} if BaseBuffer^.Tag < MAX_USER_CONFIG_DATA then begin // Very wasteful and slow 1 at a time but it is easy ConfigMemBuffer^.Address := BaseBuffer^.Tag; ConfigMemBuffer^.DataCount := 1; AppCallback_Configuration_Read(Node, ConfigMemBuffer); DataBytes[i] := ConfigMemBuffer^.DataBytes[0]; if DataBytes[i] = #0 then BaseBuffer^.StateMachine := STATE_ACDI_DONE else if BaseBuffer^.Tag = MAX_USER_CONFIG_DATA - 1 then DataBytes[i] := #0; Inc(i); Inc(BaseBuffer^.Tag); end else BaseBuffer^.StateMachine := STATE_ACDI_DONE; end; STATE_ACDI_DONE : begin {$IFDEF TRACE_SNIP}UART1_Write_Text('STATE_ACDI_DONE'+LF); {$ENDIF} {$IFDEF ETHERNET_BUS} CAN_Engine.TransmitImmediately := True; {$ENDIF} end; end; end; if i > 0 then TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_SIMPLE_NODE_INFO_REPLY, BaseBuffer^.Alias, i, @DataBytes, False, $00); if BaseBuffer^.StateMachine >= STATE_ACDI_DONE then begin NMRAnetUtilities_BaseBufferUnLink(Node, BaseBuffer); NMRAnetBufferPools_ReleaseBaseBuffer(BaseBuffer); end; NMRAnetBufferPools_ReleaseConfigMemBuffer(ConfigMemBuffer); end end; {$ENDIF} // BOOTLOADER end; end; DataBuffer := NMRAnetUtilities_NextDataBuffer(Node); if DataBuffer <> nil then begin {$IFDEF TRACTION_PROTOCOL} NMRAnetTractionProtocol_RunProtocol(Node, DataBuffer); {$ENDIF} end; // See if there are any Events to service if NMRABusTxBufferAvailable then begin if NMRAnetNode_IsAnyConsumerEventSet(Node) then begin EventIndex := NMRAnetNode_NextConsumerEventFlag(Node, State); if EventIndex > -1 then begin if NMRAnetNode_TestStateFlag(Node, NS_VIRTUAL) then begin {$IFDEF SUPPORT_AT_LEAST_ONE_VNODE_CONSUMED_EVENT} DataBytesPtr := PCAN_DataBytes( @SUPPORTED_VNODE_EVENTS_CONSUMED[EventIndex]); case State of EVENT_STATE_UNKOWN : TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_CONSUMER_IDENTIFIED_UNKNOWN, 0, 8, DataBytesPtr, False, $00); EVENT_STATE_VALID : TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_CONSUMER_IDENTIFIED_SET, 0, 8, DataBytesPtr, False, $00); EVENT_STATE_INVALID : TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_CONSUMER_IDENTIFIED_CLEAR, 0, 8, DataBytesPtr, False, $00); end; {$ENDIF} end else begin {$IFDEF SUPPORT_AT_LEAST_ONE_CONSUMED_EVENT} DataBytesPtr := PCAN_DataBytes( @SUPPORTED_EVENTS_CONSUMED[EventIndex]); case State of EVENT_STATE_UNKOWN : TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_CONSUMER_IDENTIFIED_UNKNOWN, 0, 8, DataBytesPtr, False, $00); EVENT_STATE_VALID : TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_CONSUMER_IDENTIFIED_SET, 0, 8, DataBytesPtr, False, $00); EVENT_STATE_INVALID : TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_CONSUMER_IDENTIFIED_CLEAR, 0, 8, DataBytesPtr, False, $00); end; {$ENDIF} end; end end else if NMRAnetNode_IsAnyProducerEventSet(Node) then begin State := 0; // Suppress warnings EventIndex := NMRAnetNode_NextProducerEventFlag(Node, State); if EventIndex > -1 then begin if NMRAnetNode_TestStateFlag(Node, NS_VIRTUAL) then begin {$IFDEF SUPPORT_AT_LEAST_ONE_VNODE_CONSUMED_EVENT} DataBytesPtr := PCAN_DataBytes( @SUPPORTED_VNODE_EVENTS_PRODUCED[EventIndex]); case State of EVENT_STATE_UNKOWN : TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_PRODUCER_IDENTIFIED_UNKNOWN, 0, 8, DataBytesPtr, False, $00); EVENT_STATE_VALID : TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_PRODUCER_IDENTIFIED_SET, 0, 8, DataBytesPtr, False, $00); EVENT_STATE_INVALID : TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_PRODUCER_IDENTIFIED_CLEAR, 0, 8, DataBytesPtr, False, $00); end; {$ENDIF} end else begin {$IFDEF SUPPORT_AT_LEAST_ONE_CONSUMED_EVENT} DataBytesPtr := PCAN_DataBytes( @SUPPORTED_EVENTS_PRODUCED[EventIndex]); case State of EVENT_STATE_UNKOWN : TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_PRODUCER_IDENTIFIED_UNKNOWN, 0, 8, DataBytesPtr, False, $00); EVENT_STATE_VALID : TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_PRODUCER_IDENTIFIED_SET, 0, 8, DataBytesPtr, False, $00); EVENT_STATE_INVALID : TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_PRODUCER_IDENTIFIED_CLEAR, 0, 8, DataBytesPtr, False, $00); end; {$ENDIF} end end end; end; if NMRABusTxBufferAvailable then begin if NMRAnetNode_IsAnyPCER_Set(Node) then begin EventIndex := NMRAnetNode_NextPCER_Flag(Node); if EventIndex > -1 then begin if NMRAnetNode_TestStateFlag(Node, NS_VIRTUAL) then begin {$IFDEF SUPPORT_AT_LEAST_ONE_VNODE_CONSUMED_EVENT} DataBytesPtr := PCAN_DataBytes( @SUPPORTED_VNODE_EVENTS_PRODUCED[EventIndex]); TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_PC_EVENT_REPORT, 0, 8, DataBytesPtr, False, $00); {$ENDIF} end else begin {$IFDEF SUPPORT_AT_LEAST_ONE_CONSUMED_EVENT} DataBytesPtr := PCAN_DataBytes( @SUPPORTED_EVENTS_PRODUCED[EventIndex]); TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_PC_EVENT_REPORT, 0, 8, DataBytesPtr, False, $00); {$ENDIF} end; end end end; // See if there is a Configuration Read/Write Buffer to service ConfigMemBuffer := NMRAnetUtilities_NextConfigMemBuffer(Node); if ConfigMemBuffer <> nil then begin case ConfigMemBuffer^.StateMachine of STATE_MEM_CONFIG_READWRITE_START : begin {$IFDEF TRACE_MEM_CONFIG_STATEMACHINE} UART1_Write_Text('STATE_MEM_CONFIG_READWRITE_START'+LF); {$ENDIF} case ConfigMemBuffer^.Action of CBA_READ : ConfigMemBuffer^.StateMachine := STATE_MEM_CONFIG_READWRITE_READ; CBA_WRITE : ConfigMemBuffer^.StateMachine := STATE_MEM_CONFIG_READWRITE_WRITE; CBA_WRITE_UNDER_MASK : ConfigMemBuffer^.StateMachine := STATE_MEM_CONFIG_READWRITE_WRITE_UNDER_MASK; // ConfigmemBufferAction is a Write under Mask; CBA_READ_STREAM, CBA_WRITE_STREAM : ConfigMemBuffer^.StateMachine := STATE_MEM_CONFIG_READWRITE_STREAM_REPLY_SETUP else ConfigMemBuffer^.StateMachine := STATE_MEM_CONFIG_RELEASE; end; end; STATE_MEM_CONFIG_READWRITE_READ : begin {$IFDEF TRACE_MEM_CONFIG_STATEMACHINE} UART1_Write_Text('STATE_MEM_CONFIG_READWRITE_READ'+LF); {$ENDIF} if not AppCallback_AddressSpace_Read(Node, ConfigMemBuffer) then begin case ConfigMemBuffer^.AddressSpace of MSI_CDI : begin {$IFDEF SUPPORT_VIRTUAL_NODES} if Node^.State and NS_VIRTUAL <> 0 then begin for i := 0 to ConfigMemBuffer^.DataCount - 1 do ConfigMemBuffer^.DataBytes[i] := CDI_ARRAY_VNODE[i+ConfigMemBuffer^.Address] end else {$ENDIF} begin for i := 0 to ConfigMemBuffer^.DataCount - 1 do ConfigMemBuffer^.DataBytes[i] := CDI_ARRAY[i+ConfigMemBuffer^.Address] end; end; MSI_ALL : begin ByteArray := PByteArray( ALL_MAP.LowMem); for i := 0 to ConfigMemBuffer^.DataCount - 1 do ConfigMemBuffer^.DataBytes[i] := ByteArray^[i+ConfigMemBuffer^.Address]; end; MSI_CONFIG : AppCallback_Configuration_Read(Node, ConfigMemBuffer); MSI_ACDI_MFG : begin if ConfigMemBuffer^.Address = 0 then begin Dec(ConfigMemBuffer^.DataCount); ConfigMemBuffer^.DataOffset := 1; ConfigMemBuffer^.DataBytes[0] := ACDI_MFG_VERSION; end; {$IFDEF SUPPORT_VIRTUAL_NODES} if Node^.State and NS_VIRTUAL <> 0 then begin for i := 0 to ConfigMemBuffer^.DataCount - 1 do ConfigMemBuffer^.DataBytes[i + ConfigMemBuffer^.DataOffset] := ACDI_MFG_STRINGS_VNODE[ConfigMemBuffer^.Address + i]; end else {$ENDIF} begin for i := 0 to ConfigMemBuffer^.DataCount - 1 do ConfigMemBuffer^.DataBytes[i + ConfigMemBuffer^.DataOffset] := ACDI_MFG_STRINGS[ConfigMemBuffer^.Address + i]; end; if ConfigMemBuffer^.Address = 0 then begin ConfigMemBuffer^.DataOffset := 1; Inc(ConfigMemBuffer^.DataCount); end; end; MSI_ACDI_USER : begin if ConfigMemBuffer^.Address = 0 then begin ConfigMemBuffer^.DataBytes[0] := ACDI_USER_VERSION; Inc(ConfigMemBuffer^.Address); // The CDI offsets the Configuration Memory by 1 so everything needs to be shifted Dec(ConfigMemBuffer^.DataCount); ConfigMemBuffer^.DataOffset := 1; // Write to the array at offset 1 AppCallback_Configuration_Read(Node, ConfigMemBuffer); Inc(ConfigMemBuffer^.DataCount); // Restore the state ConfigMemBuffer^.DataOffset := 0; Dec(ConfigMemBuffer^.Address); end else begin Inc(ConfigMemBuffer^.Address); // The CDI offsets the Configuration Memory by 1 so everything needs to be shifted AppCallback_Configuration_Read(Node, ConfigMemBuffer); Dec(ConfigMemBuffer^.Address); end end; end; end; ConfigMemBuffer^.StateMachine := STATE_MEM_CONFIG_READWRITE_WAIT_UNTIL_DONE_PROCESSING end; STATE_MEM_CONFIG_READWRITE_WRITE : begin {$IFDEF TRACE_MEM_CONFIG_STATEMACHINE} UART1_Write_Text('STATE_MEM_CONFIG_READWRITE_WRITE'+LF); {$ENDIF} if not AppCallback_AddressSpace_Write(Node, ConfigMemBuffer) then begin case ConfigMemBuffer^.AddressSpace of MSI_CONFIG : AppCallback_Configuration_Write(Node, ConfigMemBuffer); MSI_ACDI_USER : UART1_Write_Text('Unimplemented Configuration Mem write'+LF); end; end; ConfigMemBuffer^.StateMachine := STATE_MEM_CONFIG_READWRITE_WAIT_UNTIL_DONE_PROCESSING end; STATE_MEM_CONFIG_READWRITE_WAIT_UNTIL_DONE_PROCESSING : begin {$IFDEF TRACE_MEM_CONFIG_STATEMACHINE} UART1_Write_Text('STATE_MEM_CONFIG_READWRITE_WAIT'+LF); {$ENDIF} AppCallback_Configuration_CheckForComplete(Node, ConfigMemBuffer); // Let the app flag the configuration action complete (Clear the CBS_PROCESSING flag if conplete) if ConfigMemBuffer^.State and CBS_PROCESSING <> CBS_PROCESSING then begin DatagramBuffer := nil; if NMRAnetBufferPools_AllocateDatagramBuffer(DatagramBuffer, True) then begin DatagramBuffer^.State := DatagramBuffer^.State or CBS_PROCESSING; // Start the Transfer Immediately in the main message loop NMRAnetUtilities_DatagramBufferLink(Node, DatagramBuffer); EncodeConfigMemReadWriteHeader(Node, DatagramBuffer, ConfigMemBuffer); // Copy the data back into a Datagram and let the node send it when it can ConfigMemBuffer^.StateMachine := STATE_MEM_CONFIG_RELEASE; // This is updated within the CAN Interrupt by detecting if the target received the datagram correctly or not // and handling a resend. Once done the datagram is released and unlinked from the node and no other action is need in the statemachine end end end; STATE_MEM_CONFIG_READWRITE_WRITE_UNDER_MASK : begin ConfigMemBuffer^.StateMachine := STATE_MEM_CONFIG_RELEASE; end; STATE_MEM_CONFIG_READWRITE_STREAM_REPLY_SETUP : begin if NMRAnetBufferPools_AllocateDatagramBuffer(DatagramBuffer, True) then // Create a new datagram to send the Read Reply before opening the Read Stream begin NMRAnetUtilities_DatagramBufferLink(Node, DatagramBuffer); EncodeConfigMemReadWriteHeader(Node, DatagramBuffer, ConfigMemBuffer); // Copy the data back into a Datagram and let the node send it when it can DatagramBuffer^.State := DatagramBuffer^.State or CBS_PROCESSING; // Start the Transfer of the Read/Write Reply Immediately ConfigMemBuffer^.Stream^.State := ConfigMemBuffer^.Stream^.State or CBS_PROCESSING; // Start the streaming statemachine if ConfigMemBuffer^.Action = CBA_READ_STREAM then ConfigMemBuffer^.StateMachine := STATE_MEM_CONFIG_READWRITE_STREAM_READ else ConfigMemBuffer^.StateMachine := STATE_MEM_CONFIG_READWRITE_STREAM_WRITE end end; STATE_MEM_CONFIG_READWRITE_STREAM_READ : begin // Setup the Stream to read and then wait for its statemachine to complete if ConfigMemBuffer^.Stream^.StateMachine = STATE_STREAM_COMPLETE then ConfigMemBuffer^.StateMachine := STATE_MEM_CONFIG_RELEASE; end; STATE_MEM_CONFIG_READWRITE_STREAM_WRITE : begin end; STATE_MEM_CONFIG_RELEASE : begin NMRAnetUtilities_ConfigMemBufferUnLink(Node, ConfigMemBuffer); NMRAnetUtilities_StreamBufferUnLink(Node, ConfigMemBuffer^.Stream); // Ok to send a nil to this function NMRAnetBufferPools_ReleaseStreamBuffer(ConfigMemBuffer^.Stream); NMRAnetBufferPools_ReleaseConfigMemBuffer(ConfigMemBuffer); end; end end; // See if there is a Datagram Buffer to service DatagramBuffer := NMRAnetUtilities_NextDatagramBuffer(Node); // Grab the next completed Datagram to work on if DatagramBuffer <> nil then begin {$IFDEF TRACE_BUFFER_ADDRESSES} UART1_Write_Text('DatagramBuffer Statemachine :'); WordToHex(DatagramBuffer, s1); UART1_Write_Text('$'+s1+LF); {$ENDIF} case DatagramBuffer^.mCode of {$IFDEF BOOTLOADER} BMC_DATAGRAM_BOOTLOADER : begin if NMRABusTxBufferAvailable then TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_DATAGRAM_OK_REPLY, DatagramBuffer^.Alias, 0, @DataBytes, False); end; {$ENDIF} BMC_DATAGRAM_MEMORY_CONFIG : begin case DatagramBuffer^.StateMachine of STATE_DATAGRAM_MEM_CONFIG_SEND_REPLY : // CAN Receive does not handle the Ack to the Config Mem request, it is done here for better control begin {$IFDEF TRACE_MEM_CONFIG_STATEMACHINE} UART1_Write_Text('STATE_DATAGRAM_MEM_CONFIG_SEND_REPLY'+LF); {$ENDIF} if NMRABusTxBufferAvailable then // When a Datagram is received the ACK is not sent at that time. It is packed up in a DatagramBuffer and begin // passed to the main statemachine, here. It is this sub-statemachines job to send the correct ACK case DatagramBuffer^.DataBytes[1] and MCP_COMMAND_MASK of MCP_COMMAND_READ, MCP_COMMAND_READ_STREAM, MCP_COMMAND_WRITE, MCP_COMMAND_WRITE_STREAM : // Convert in to a ConfigMemBuffer and add it to the list to process begin FailDatagram := False; if Node^.ConfigMemBuffers <> nil then // Only allow 1 Read/Write access at a time for each node begin if NMRAnetBufferPools_AllocateConfigMemBuffer(ConfigMemBuffer) then // Get a Config Mem Buffer, this locks up new Config Reads and Writes begin if DecodeConfigMemReadWriteHeader(Node, DatagramBuffer, ConfigMemBuffer) then // Decode the Datagram into the ConfigMemBuffer Fields, unless something goes wrong begin NMRAnetUtilities_ConfigMemBufferLink(Node, ConfigMemBuffer); // Link it to the node so the statemachines will run AppCallback_ConfigMemReadWriteAckFlags(Node, ConfigMemBuffer); // How to handle the ACK is up to the app if ConfigMemBuffer^.AckFlags <> 0 then // Note this ACk reply is not a failure, it is the new byte that can be returned begin DataBytes[0] := ConfigMemBuffer^.AckFlags; TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_DATAGRAM_OK_REPLY, DatagramBuffer^.Alias, 1, @DataBytes, False, $00); end else TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_DATAGRAM_OK_REPLY, DatagramBuffer^.Alias, 0, @DataBytes, False, $00); end else FailDatagram := True; end else FailDatagram := True; end else FailDatagram := True; if FailDatagram then begin DataBytes[0] := DATAGRAM_RESULT_REJECTED_BUFFER_FULL[0]; DataBytes[1] := DATAGRAM_RESULT_REJECTED_BUFFER_FULL[1]; TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_DATAGRAM_REJECTED_REPLY, DatagramBuffer^.Alias, 2, @DataBytes, False, $00); end; // Unlink regardless of what we do as we either have failed it or moved over to a ConfigMemBuffer NMRAnetUtilities_DatagramBufferUnLink(Node, DatagramBuffer); // Copied the info to the ConfigMemBuffer, release the Datagram for now NMRAnetBufferPools_ReleaseDatagramBuffer(DatagramBuffer); // This is the end of this statemachine, it is continued in the Config Mem Statemachine end; MCP_OPERATION : begin DataBytes[0] := Hi( Node^.Info.AliasID); DataBytes[1] := Lo( Node^.Info.AliasID); TransmitNMRABusLayerMsg(Node, CANBuffer, MTI_DATAGRAM_OK_REPLY, DatagramBuffer^.Alias, 0, @DataBytes, False, $00); // transmit the reply // We will reuse the Datagram Buffer for this one case DatagramBuffer^.DataBytes[1] of // Mask off the upper 2 bits MCP_OP_GET_CONFIG : begin {$IFDEF TRACE_MEM_CONFIG_OP_STATEMACHINE} UART1_Write_Text('MCP_OP_GET_CONFIG'+LF);{$ENDIF} DatagramBuffer^.DataBytes[0] := DATAGRAM_TYPE_MEMORY_CONFIGURATION; DatagramBuffer^.DataBytes[1] := MCP_OP_GET_CONFIG_REPLY; {$IFDEF SUPPORT_VIRTUAL_NODES} if NMRAnetNode_TestStateFlag(Node, NS_VIRTUAL) then begin DatagramBuffer^.DataBytes[2] := Hi( MEMORY_CONFIG_OPTIONS_VNODE.MemoryConfigOptions); DatagramBuffer^.DataBytes[3] := Lo( MEMORY_CONFIG_OPTIONS_VNODE.MemoryConfigOptions); DatagramBuffer^.DataBytes[4] := MEMORY_CONFIG_OPTIONS_VNODE.MemoryConfigWriteLength; DatagramBuffer^.DataBytes[5] := MEMORY_CONFIG_OPTIONS_VNODE.MemoryConfigHighestSpace; DatagramBuffer^.DataBytes[6] := MEMORY_CONFIG_OPTIONS_VNODE.MemoryConfigLowestSpace; end else {$ENDIF} begin DatagramBuffer^.DataBytes[2] := Hi( MEMORY_CONFIG_OPTIONS.MemoryConfigOptions); DatagramBuffer^.DataBytes[3] := Lo( MEMORY_CONFIG_OPTIONS.MemoryConfigOptions); DatagramBuffer^.DataBytes[4] := MEMORY_CONFIG_OPTIONS.MemoryConfigWriteLength; DatagramBuffer^.DataBytes[5] := MEMORY_CONFIG_OPTIONS.MemoryConfigHighestSpace; DatagramBuffer^.DataBytes[6] := MEMORY_CONFIG_OPTIONS.MemoryConfigLowestSpace; end; DatagramBuffer^.iByteCount := 7; // Send a response DatagramBuffer^.Statemachine := STATE_DATAGRAM_MEM_CONFIG_SEND_RESUSED_DATAGRAM end; MCP_OP_GET_ADD_SPACE_INFO : begin {$IFDEF TRACE_MEM_CONFIG_OP_STATEMACHINE} UART1_Write_Text('MCP_OP_GET_ADD_SPACE_INFO'+LF);{$ENDIF} DatagramBuffer^.DataBytes[0] := DATAGRAM_TYPE_MEMORY_CONFIGURATION; DatagramBuffer^.DataBytes[1] := MCP_OP_GET_ADD_SPACE_INFO_REPLY; if AppCallback_AddressSpacePresent(Node, DatagramBuffer^.DataBytes[2]) then DatagramBuffer^.DataBytes[1] := DatagramBuffer^.DataBytes[1] or MCP_OP_GET_ADD_SPACE_INFO_REPLY_PRESENT; DatagramBuffer^.DataBytes[2] := DatagramBuffer^.DataBytes[2]; // I am not supporting the ability to return anything but a $0 for the lower address so we only deal with offsets from zero in these calls MemorySpaceMaxAddress := MaxAddressByAddressSpace(Node, DatagramBuffer^.DataBytes[2]); DatagramBuffer^.DataBytes[3] := (DWord(MemorySpaceMaxAddress) shr 24) and $000000FF; DatagramBuffer^.DataBytes[4] := (DWord(MemorySpaceMaxAddress) shr 16) and $000000FF; DatagramBuffer^.DataBytes[5] := (DWord(MemorySpaceMaxAddress) shr 8) and $000000FF; DatagramBuffer^.DataBytes[6] := DWord(MemorySpaceMaxAddress) and $000000FF; if AppCallback_AddressSpaceReadOnly(Node, DatagramBuffer^.DataBytes[2]) then DatagramBuffer^.DataBytes[7] := $01 else DatagramBuffer^.DataBytes[7] := $00; DatagramBuffer^.iByteCount := 8; // Send a response DatagramBuffer^.Statemachine := STATE_DATAGRAM_MEM_CONFIG_SEND_RESUSED_DATAGRAM end; MCP_OP_LOCK : begin {$IFDEF TRACE_MEM_CONFIG_OP_STATEMACHINE} UART1_Write_Text('MCP_OP_LOCK'+LF);{$ENDIF} NMRAnetUtilities_DatagramBufferUnLink(Node, DatagramBuffer); // Unknown Command just throw it away NMRAnetBufferPools_ReleaseDatagramBuffer(DatagramBuffer); end; MCP_OP_GET_UNIQUEID : begin {$IFDEF TRACE_MEM_CONFIG_OP_STATEMACHINE} UART1_Write_Text('MCP_OP_GET_UNIQUEID'+LF);{$ENDIF} NMRAnetUtilities_DatagramBufferUnLink(Node, DatagramBuffer); // Unknown Command just throw it away NMRAnetBufferPools_ReleaseDatagramBuffer(DatagramBuffer); end; MCP_OP_FREEZE : begin {$IFDEF TRACE_MEM_CONFIG_OP_STATEMACHINE} UART1_Write_Text('MCP_OP_FREEZE'+LF);{$ENDIF} NMRAnetUtilities_DatagramBufferUnLink(Node, DatagramBuffer); // Unknown Command just throw it away NMRAnetBufferPools_ReleaseDatagramBuffer(DatagramBuffer); end; MCP_OP_INDICATE : begin {$IFDEF TRACE_MEM_CONFIG_OP_STATEMACHINE} UART1_Write_Text('MCP_OP_INDICATE'+LF);{$ENDIF} NMRAnetUtilities_DatagramBufferUnLink(Node, DatagramBuffer); // Unknown Command just throw it away NMRAnetBufferPools_ReleaseDatagramBuffer(DatagramBuffer); end; MCP_OP_UPDATE_COMPLETE : begin {$IFDEF TRACE_MEM_CONFIG_OP_STATEMACHINE} UART1_Write_Text('MCP_OP_UPDATE_COMPLETE'+LF);{$ENDIF} NMRAnetUtilities_DatagramBufferUnLink(Node, DatagramBuffer); // Unknown Command just throw it away NMRAnetBufferPools_ReleaseDatagramBuffer(DatagramBuffer); end; MCP_OP_RESETS : begin {$IFDEF TRACE_MEM_CONFIG_OP_STATEMACHINE} UART1_Write_Text('MCP_OP_RESETS'+LF);{$ENDIF} asm reset; end; end else begin {$IFDEF TRACE_MEM_CONFIG_OP_STATEMACHINE} UART1_Write_Text('UNKNOWN MEM CONFIG OPERATION'+LF);{$ENDIF} // Don't know what that was but we got it NMRAnetUtilities_DatagramBufferUnLink(Node, DatagramBuffer); // Unknown Command just throw it away NMRAnetBufferPools_ReleaseDatagramBuffer(DatagramBuffer); end; end end else begin NMRAnetUtilities_DatagramBufferUnLink(Node, DatagramBuffer); // Unknown Command just throw it away NMRAnetBufferPools_ReleaseDatagramBuffer(DatagramBuffer); end; end; // Case end; end; STATE_DATAGRAM_MEM_CONFIG_SEND_RESUSED_DATAGRAM : begin {$IFDEF TRACE_MEM_CONFIG_STATEMACHINE} UART1_Write_Text('STATE_DATAGRAM_MEM_CONFIG_SEND_RESUSED_DATAGRAM'+LF); {$ENDIF} // This is safe because this will not be called until CBS_PROCESSSING is cleared simulating a "WaitForSendComplete" DatagramBuffer^.State := (DatagramBuffer^.State or CBS_OUTGOING or CBS_PROCESSING) and not CBS_TRANSFER_COMPLETE; // Turn it into an outgoing Datagram DatagramBuffer^.Tag := 0; // Reset for the Transmit side DatagramBuffer^.iWatchdog := 0; DatagramBuffer^.Statemachine := STATE_DATAGRAM_MEM_CONFIG_WAITFOR_REPLY; // Waiting for the target to reply it received the datagram successfully end; STATE_DATAGRAM_MEM_CONFIG_WAITFOR_REPLY : begin {$IFDEF TRACE_MEM_CONFIG_STATEMACHINE} UART1_Write_Text('STATE_DATAGRAM_MEM_CONFIG_WAITFOR_REPLY'+LF); {$ENDIF} // This is updated within the CAN Interrupt by detecting if the target received the datagram correctly or not // and handling a resend. Once done the datagram is released and unlinked from the node and no other action is need in the statemachine {$IFDEF TRACE_CONFIG_MEM_DECODE} UART1_Write_Text('After'+LF); ByteToStr(ConfigMemBuffer^.AddressSpace, s1); UART1_Write_Text('MemorySpace= '+s1+LF); LongWordToStr(MemorySpaceMaxAddress, s1); UART1_Write_Text('MemorySpaceMaxAddress= '+s1+LF); LongWordToStr(MemorySpaceAddress, s1); UART1_Write_Text('MemorySpaceAddress= '+s1+LF); ShortToStr(ConfigMemBuffer^.DataCount, s1); UART1_Write_Text('MemorySpaceCount= '+s1+LF); {$ENDIF} end; end; // Case end else begin NMRAnetUtilities_DatagramBufferUnLink(Node, DatagramBuffer); // Unknown Command just throw it away NMRAnetBufferPools_ReleaseDatagramBuffer(DatagramBuffer); end; end; end; // See if there is a Stream Buffer to service StreamBuffer := NMRAnetUtilities_NextStreamBuffer(Node); // Grab the next completed Stream to work on if StreamBuffer <> nil then begin {$IFDEF TRACE_BUFFER_ADDRESSES} UART1_Write_Text('StreamBuffer Statemachine :'); WordToHex(StreamBuffer, s1); UART1_Write_Text('$'+s1+LF); {$ENDIF} NMRAnetUtilities_StreamBufferUnLink(Node, StreamBuffer); // Unknown Command just throw it away NMRAnetBufferPools_ReleaseStreamBuffer(StreamBuffer); end; end; end; // ***************************************************************************** // procedure ProcessOutgoingDatagrams // Parameters: // Returns: // // Description: If outgoing messages need to be queued up then this function // ensures all needed messages are sent; // Expects CAN to be disabled // // ***************************************************************************** procedure ProcessOutgoingDatagrams(Node: PNMRAnetNode; CANBuffer: PCANBuffer); var DatagramBuffer: PDatagramBuffer; begin // Look for outgoing Datagrams DatagramBuffer := nil; if NMRAnetUtilities_FindOutgoingDatagramInNode(Node, DatagramBuffer, True) then begin {$IFDEF TRACE_DATAGRAM_SEND}UART1_Write_Text('NMRAnetUtilities_FindOutgoingDatagram = Found'+LF);{$ENDIF} if NMRAnetStateMachine_TrySendDatagram(Node, CANBuffer, DatagramBuffer) then begin {$IFDEF TRACE_DATAGRAM_SEND}UART1_Write_Text('NMRAnetStateMachine_TrySendDatagram = Sent'+LF);{$ENDIF} DatagramBuffer^.iWatchdog := 0; // Reset watchdog, now that it is complete it can timout waiting for the ACK/NAK DatagramBuffer^.State := (DatagramBuffer^.State and not CBS_PROCESSING) or CBS_TRANSFER_COMPLETE; // Flag as complete (this is also flagged as outgoing) so the Statemachine can wait for a response from the target end; end; end; // ***************************************************************************** // procedure ProcessOutgoingStreams // Parameters: // Returns: // // Description: If outgoing messages need to be queued up then this function // ensures all needed messages are sent; // Expects CAN to be disabled // // ***************************************************************************** procedure ProcessOutgoingStreams(Node: PNMRAnetNode; CANBuffer: PCANBuffer); var StreamBuffer: PStreamBuffer; begin StreamBuffer := nil; if NMRAnetUtilities_FindOutgoingStreamInNode(Node, StreamBuffer, True) then // Find a Stream that is allocated and waiting to send a message out begin case StreamBuffer^.StateMachine of STATE_STREAM_INITIATE_REQUEST : // We want to send, this expects the StreamBuffer to be setup begin if NMRAnetStateMachine_TrySendStreamInitiate(Node, CANBuffer, StreamBuffer) then begin StreamBuffer^.iWatchdog := 0; StreamBuffer^.State := StreamBuffer^.State and not CBS_OUTGOING; // When the Reply is received it will jump to Send, since we removed OutGoing StreamBuffer^.StateMachine := STATE_STREAM_IDLE; // Wait for the Initate Reply end; end; STATE_STREAM_INITIATE_REQEUST_WAIT_FOR_REPLY : begin end; STATE_STREAM_INITIATE_REPLY : // Remote node as asked to send us data begin if NMRAnetStateMachine_TrySendStreamInitiateReply(Node, CANBuffer, StreamBuffer) then begin StreamBuffer^.iWatchdog := 0; StreamBuffer^.State := StreamBuffer^.State and not CBS_OUTGOING; StreamBuffer^.StateMachine := STATE_STREAM_IDLE; // Wait for it to send the data to us end; end; STATE_STREAM_SEND : // Send what we need to send begin end; end; end; end; // ***************************************************************************** // procedure ProcessMarkedForDeleteNodes // Parameters: // Returns: // Description: // ***************************************************************************** procedure ProcessMarkedForDeleteNodes(Node: PNMRAnetNode; CANBuffer: PCANBuffer); var i, j: Integer; DoDeAllocate: Boolean; begin if NMRAnetNode_TestStateFlag(Node, NS_RELEASING) then begin // Make sure all NMRAnet messages are sent and we have an empty CAN buffer if NMRABusTxBufferAvailable and CANBusBufferAvailable then begin if NMRAnetNode_TestStateFlag(Node, NS_PERMITTED) then begin // If the node is in the Permitted state than make sure the node sends all the flag it needs to. DoDeallocate := False; if not NMRAnetNode_IsAnyConsumerEventSet(Node) then if not NMRAnetNode_IsAnyProducerEventSet(Node) then if not NMRAnetNode_IsAnyPCER_Set(Node) then if (Node^.MsgFlags = 0) then if (Node^.MsgFlagsUserDefined = 0) then if (Node^.BaseBuffers = nil) then if (Node^.DatagramBuffers = nil) then begin TransmitCANLayerMsg(Node, CANBuffer, MTI_AMR); DoDeallocate := True; end; end else DoDeallocate := True; // If it is not in the Permitted state then we are not allowed to send a AMR so just free it if DoDeallocate then begin i := 0; while i < Nodes.AllocatedCount do // Search the Allocated List to remove it if it has made it into the list begin if Nodes.AllocatedList[i] = Node then // Found the node begin Nodes.AllocatedList[i] := PNMRAnetNode( nil); // Nil it in the Allocated List j := i; while j < Nodes.AllocatedCount - 1 do // Now Pack the list, moving higher indexed Nodes down one begin Nodes.AllocatedList[j] := Nodes.AllocatedList[j + 1]; Nodes.AllocatedList[j + 1] := PNMRAnetNode( nil); Inc(j); end; Dec(Nodes.AllocatedCount); i := Nodes.AllocatedCount; // Done, break out end; Inc(i); end; Node^.State := NS_EMPTY // Do this last so item is not allocated in an interrupt half way through this end; end; end; end; // ***************************************************************************** // procedure ProcessAbandonBuffers // Parameters: // Returns: // // Description: // // ***************************************************************************** procedure ProcessAbandonBuffers(Node: PNMRAnetNode); const CBS_OUTGOING_COMPLETE = CBS_TRANSFER_COMPLETE or CBS_OUTGOING; var DatagramBuffer: PDatagramBuffer; StreamBuffer: PStreamBuffer; begin DatagramBuffer := Node^.DatagramBuffers; while DatagramBuffer <> nil do begin if (DatagramBuffer^.State and CBS_OUTGOING_COMPLETE = CBS_OUTGOING_COMPLETE) or // The target of the outgoing may never send us a response so bail out ((DatagramBuffer^.State and CBS_OUTGOING = 0) and (DatagramBuffer^.State and CBS_PROCESSING = CBS_PROCESSING)) // An incoming only times out during the reception, once complete it waits until we can use it then begin if DatagramBuffer^.iWatchdog >= DATAGRAM_WATCHDOG_MAX then begin UART_Write_Text('Abandon Datagram'+LF); NMRAnetUtilities_DatagramBufferUnLink(Node, DatagramBuffer); NMRAnetBufferPools_ReleaseDatagramBuffer(DatagramBuffer); end; end; DatagramBuffer := DatagramBuffer^.Next end; StreamBuffer := Node^.StreamBuffers; while StreamBuffer <> nil do begin if (StreamBuffer^.State and CBS_OUTGOING_COMPLETE = CBS_OUTGOING_COMPLETE) or // The target of the outgoing may never send us a response so bail out ((StreamBuffer^.State and CBS_OUTGOING = 0) and (StreamBuffer^.State and CBS_PROCESSING = CBS_PROCESSING)) // An incoming only times out during the reception, once complete it waits until we can use it then begin if StreamBuffer^.iWatchdog >= STREAM_WATCHDOG_MAX then begin UART_Write_Text('Abandon Stream'+LF); NMRAnetUtilities_StreamBufferUnLink(Node, StreamBuffer); NMRAnetBufferPools_ReleaseStreamBuffer(StreamBuffer); end; end; StreamBuffer := StreamBuffer^.Next end; end; // ***************************************************************************** // procedure NMRAnetStateMachine_Process // Parameters: // Returns: // // Description: // // ***************************************************************************** procedure NMRAnetStateMachine_Process(Node: PNMRAnetNode); var CANBuffer: TCANBuffer; VariableField: DWord; begin case Node^.iStateMachine of STATE_NMRABUS_START : begin {$IFDEF TRACE_NODE_STATEMACHINE} UART1_Write_Text('STATE_NMRABUS_START'+LF); {$ENDIF} LockCANInterrupt; Node^.Login.iCID := 0; Node^.iStateMachine := STATE_NMRABUS_TRANSMIT_CID; UnLockCANInterrupt end; STATE_NMRABUS_GENERATE_NODE_ALIAS : begin {$IFDEF TRACE_NODE_STATEMACHINE} UART1_Write_Text('STATE_NMRABUS_GENERATE_NODE_ALIAS'+LF); {$ENDIF} LockCANInterrupt; Node^.Info.AliasID := NMRAnetUtilities_CreateAliasID(Node^.Info.Seed, False); NMRAnetNode_SortNodeList(Nodes); Node^.Login.iCID := 0; Node^.iStateMachine := STATE_NMRABUS_TRANSMIT_CID; UnLockCANInterrupt; end; STATE_RANDOM_NUMBER_GENERATOR : begin {$IFDEF TRACE_NODE_STATEMACHINE} UART1_Write_Text('STATE_RANDOM_NUMBER_GENERATOR'+LF); {$ENDIF} LockCANInterrupt; NMRAnetUtilities_PsudoRandomNumberGeneratorOnSeed(Node^.Info.Seed); Node^.iStateMachine := STATE_NMRABUS_GENERATE_NODE_ALIAS; UnLockCANInterrupt end; STATE_NMRABUS_TRANSMIT_CID : begin {$IFDEF TRACE_NODE_STATEMACHINE} UART1_Write_Text('STATE_NMRABUS_TRANSMIT_CID'+LF); {$ENDIF} LockCANInterrupt; case Node^.Login.iCID of 0 : VariableField := MTI_CID0; // Queue up 1 : VariableField := MTI_CID1; 2 : VariableField := MTI_CID2; 3 : VariableField := MTI_CID3; end; UnLockCANInterrupt; if CANBusBufferAvailable then begin LockCANInterrupt; Node^.iStateMachine := STATE_NMRABUS_NEXT_CDI; UnLockCANInterrupt end end; STATE_NMRABUS_NEXT_CDI : begin {$IFDEF TRACE_NODE_STATEMACHINE} UART1_Write_Text('STATE_NMRABUS_NEXT_CDI'+LF); {$ENDIF} LockCANInterrupt; if Node^.Login.iCID < 3 then begin Inc(Node^.Login.iCID); Node^.iStateMachine := STATE_NMRABUS_TRANSMIT_CID end else begin Node^.iStateMachine := STATE_NMRABUS_WAITSTATE; // There is no way to really understand when the message will be on the CAN bus so need to have "reasonable" delay (500ms) Node^.Login.TimeCounter := 0; end; UnLockCANInterrupt end; STATE_NMRABUS_WAITSTATE : begin {$IFDEF TRACE_NODE_STATEMACHINE} UART1_Write_Text('STATE_NMRABUS_WAITSTATE'+LF); {$ENDIF} LockCANInterrupt; if Node^.Login.TimeCounter > MAX_BUS_LOGIN_TIMEOUT then Node^.iStateMachine := STATE_NMRABUS_SEND_LOGIN_RID; UnLockCANInterrupt end; STATE_NMRABUS_SEND_LOGIN_RID : begin {$IFDEF TRACE_NODE_STATEMACHINE} UART1_Write_Text('STATE_NMRABUS_SEND_LOGIN_RID'+LF); {$ENDIF} LockCANInterrupt; if NMRAnetNode_TestMsgFlags(Node, MF_DUPLICATE_ALIAS, True) then begin Node^.iStateMachine := STATE_RANDOM_NUMBER_GENERATOR; UnLockCANInterrupt end else begin UnLockCANInterrupt; if CANBusBufferAvailable then begin LockCANInterrupt; TransmitCANLayerMsg(Node, @CANBuffer, MTI_RID); Node^.iStateMachine := STATE_NMRABUS_SEND_LOGIN_AMD; UnLockCANInterrupt; end end end; STATE_NMRABUS_SEND_LOGIN_AMD : begin {$IFDEF TRACE_NODE_STATEMACHINE} UART1_Write_Text('STATE_NMRABUS_SEND_LOGIN_AMD'+LF); {$ENDIF} LockCANInterrupt; if NMRAnetNode_TestMsgFlags(Node, MF_DUPLICATE_ALIAS, True) then begin Node^.iStateMachine := STATE_RANDOM_NUMBER_GENERATOR; UnLockCANInterrupt; end else begin UnLockCANInterrupt; if CANBusBufferAvailable then begin LockCANInterrupt; TransmitCANLayerMsg(Node, @CANBuffer, MTI_AMD); NMRAnetNode_SetStateFlag(Node, NS_PERMITTED); Node^.iStateMachine := STATE_NMRABUS_INITIALIZED; UnLockCANInterrupt end end end; STATE_NMRABUS_INITIALIZED : begin {$IFDEF TRACE_NODE_STATEMACHINE} UART1_Write_Text('STATE_NMRABUS_INITIALIZED'+LF); {$ENDIF} LockCANInterrupt; if NMRAnetNode_TestMsgFlags(Node, MF_DUPLICATE_ALIAS, True) then begin Node^.iStateMachine := STATE_RANDOM_NUMBER_GENERATOR; UnLockCANInterrupt end else begin UnLockCANInterrupt; if NMRABusTxBufferAvailable then begin LockCANInterrupt; NMRAnetUtilities_LoadFrameCANBufferDataWith48BitNodeID(Node, @CANBuffer); TransmitNMRABusLayerMsg(Node, @CANBuffer, MTI_INITIALIZATION_COMPLETE, 0, 6, @CANBuffer.DataBytes, False, $00); Node^.iStateMachine := STATE_NMRABUS_LOGIN_IDENTIFY_EVENTS; NMRAnetNode_SetStateFlag(Node, NS_INITIALIZED); UnLockCANInterrupt; end end end; STATE_NMRABUS_LOGIN_IDENTIFY_EVENTS : begin {$IFDEF TRACE_NODE_STATEMACHINE} UART1_Write_Text('STATE_NMRABUS_LOGIN_IDENTIFY_EVENTS'+LF); {$ENDIF} // Fake an Identify Events to allow the AppCallbacks to be called LockCANInterrupt; CANBuffer.State := CANBuffer.State or BS_EXTENDED; CANBuffer.ID := $10000000 or MTI_EVENTS_IDENTIFY_DEST; // Make Alias ID $000 so we don't trigger a duplicate Alias ID loop! CANBuffer.DataCount := 2; CANBuffer.DataBytes[0] := Hi( Node^.Info.AliasID); // Addressed to this node CANBuffer.DataBytes[1] := Lo( Node^.Info.AliasID); ReceivedOnFilter1(@CANBuffer); Node^.iStateMachine := STATE_NMRABUS_PERMITTED; UnLockCANInterrupt; end; STATE_NMRABUS_PERMITTED : begin // {$IFDEF TRACE_NODE_STATEMACHINE} UART1_Write_Text('STATE_NMRABUS_PERMITTED'+LF); {$ENDIF} LockCANInterrupt; ProcessNode(Node, @CANBuffer); // Handle auto Actions to CAN/NMRAnet messages coming in ProcessOutgoingDatagrams(Node, @CANBuffer); // Handle outgoing Datagrams ProcessOutgoingStreams(Node, @CANBuffer); // Handle outgoing Streams ProcessMarkedForDeleteNodes(Node, @CANBuffer); // Handle vNodes marked to be deleted {$IFNDEF DISABLE_DATAGRAM_ABANDON_TIMEOUT} ProcessAbandonBuffers(Node); // Handle (free) buffers (datagrams mainly) that appear to be abandon in midstream {$ENDIF} UnLockCANInterrupt end; STATE_NMRABUS_INHIBITED : begin {$IFDEF TRACE_NODE_STATEMACHINE} UART1_Write_Text('STATE_NMRABUS_INHIBITED'+LF); {$ENDIF} LockCANInterrupt; CANStorage_FlushBuffers(Node^.Info.AliasID); UnLockCANInterrupt; if CANBusBufferAvailable then begin LockCANInterrupt; TransmitCANLayerMsg(Node, @CANBuffer, MTI_AMR); NMRAnetUtilities_ReleaseAllBuffers(Node); NMRAnetNode_ClearStateFlag(Node, NS_PERMITTED); NMRAnetNode_ClearMsgFlags(Node); Node^.iStateMachine := STATE_RANDOM_NUMBER_GENERATOR; UnLockCANInterrupt end; end; STATE_NMRABUS_DUPLICATE_FULL_ID : begin {$IFDEF TRACE_NODE_STATEMACHINE} UART1_Write_Text('STATE_NMRABUS_DUPLICATE_FULL_ID'+LF); {$ENDIF} LockCANInterrupt; CANStorage_FlushBuffers(Node^.Info.AliasID); UnLockCANInterrupt; if CANBusBufferAvailable then begin LockCANInterrupt; TransmitCANLayerMsg(Node, @CANBuffer, MTI_AMR); NMRAnetUtilities_ReleaseAllBuffers(Node); NMRAnetNode_ClearStateFlag(Node, NS_PERMITTED); NMRAnetNode_ClearMsgFlags(Node); Node^.iStateMachine := STATE_NMRABUS_TAKE_OFFLINE ; UnLockCANInterrupt; end; end; STATE_NMRABUS_TAKE_OFFLINE : begin {$IFDEF TRACE_NODE_STATEMACHINE} UART1_Write_Text('STATE_NMRABUS_TAKE_OFFLINE'+LF); {$ENDIF} if NMRABusTxBufferAvailable then begin LockCANInterrupt; TransmitNMRABusLayerMsg(Node, @CANBuffer, MTI_PC_EVENT_REPORT, 0, 8, @EVENT_DUPLICATE_ID_DETECTED, False, $00); Node^.iStateMachine := STATE_NMRABUS_OFFLINE; UnLockCANInterrupt; end end; STATE_NMRABUS_OFFLINE : begin // Done until reboot end else UART1_Write_Text('Statemachine Index Error'+LF); end; UnLockCANInterrupt end; // ***************************************************************************** // procedure NMRAnetStateMachine_Disconnect // Parameters: // Returns: // // Description: // // ***************************************************************************** procedure NMRAnetStateMachine_Disconnect(Node: PNMRAnetNode); begin Node^.iStateMachine := STATE_NMRABUS_INHIBITED end; end.