unit template_userstatemachine; {$I Options.inc} {.$DEFINE DEBUG_DISCOVER_STATEMACHINE} uses Float16, opstacktypes, opstackdefines, template_node, opstack_api, nmranetdefines, NMRAnetCabBridgeDefines, NMRAnetCabBridge, nmranetutilities; procedure UserStateMachine_Initialize; procedure AppCallback_UserStateMachine_Process(Node: PNMRAnetNode); procedure AppCallback_NodeInitialize(Node: PNMRAnetNode); procedure CabBus_UART_RX_StateMachine; procedure CabBus_Timeout; // Called every 100ms typically from another thread so only use to update flags procedure AppCallback_Timer_1s; // These message are called from the mainstatemachine loop. They have been stored in // internal storage buffers. See the notes to understand the implications of this and how to use them correctly procedure AppCallback_SimpleNodeInfoReply(Node: PNMRAnetNode; AMessage: POPStackMessage); procedure AppCallBack_ProtocolSupportReply(Node: PNMRAnetNode; AMessage: POPStackMessage); // This could be 2 replies per call.. read docs procedure AppCallback_RemoteButtonReply(Node: PNMRAnetNode; var Source: TNodeInfo; DataBytes: PSimpleBuffer); {$IFDEF SUPPORT_TRACTION} procedure AppCallback_TractionProtocol(Node: PNMRAnetNode; AMessage: POPStackMessage); procedure AppCallback_TractionProtocolReply(Node: PNMRAnetNode; AMessage: POPStackMessage); procedure AppCallback_SimpleTrainNodeInfoReply(Node: PNMRAnetNode; AMessage: POPStackMessage); {$ENDIF} {$IFDEF SUPPORT_TRACTION_PROXY} function AppCallback_TractionProxyProtocol(Node: PNMRAnetNode; AMessage: POPStackMessage; SourceHasLock: Boolean): Boolean; procedure AppCallback_TractionProxyProtocolReply(Node: PNMRAnetNode; AMessage: POPStackMessage); {$ENDIF} // These messages are called directly from the hardware receive buffer. See the notes to understand the // implications of this and how to use them correctly procedure AppCallback_InitializationComplete(var Source: TNodeInfo; NodeID: PNodeID); procedure AppCallback_VerifiedNodeID(var Source: TNodeInfo; NodeID: PNodeID); procedure AppCallback_ConsumerIdentified(var Source: TNodeInfo; MTI: Word; EventID: PEventID); procedure AppCallback_ProducerIdentified(var Source: TNodeInfo; MTI: Word; EventID: PEventID); procedure AppCallback_LearnEvent(var Source: TNodeInfo; EventID: PEventID); procedure AppCallBack_PCEventReport(var Source: TNodeInfo; EventID: PEventID); // Configuration Memory procedure AppCallBack_ConfigMemReadReply(Node: PNMRAnetNode; AMessage: POPStackMessage; Success: Boolean); procedure AppCallBack_ConfigMemStreamReadReply(Node: PNMRAnetNode; AMessage: POPStackMessage; Success: Boolean); procedure AppCallBack_ConfigMemWriteReply(Node: PNMRAnetNode; AMessage: POPStackMessage; Success: Boolean); procedure AppCallBack_ConfigMemStreamWriteReply(Node: PNMRAnetNode; AMessage: POPStackMessage; Success: Boolean); procedure Hardware_EnableInterrupts; external; procedure Hardware_DisableInterrupts; external; const FUNCTION_HORN = 1; FUNCTION_BELL = 2; const CONFIG_OFFSET_SPEED_STEP = 128; CONFIG_OFFSET_ADDRESS_TYPE = 129; const STATE_CAB_IDLE = 1; STATE_CAB_SELECT_LOCO = 2; STATE_CAB_RUN_MACRO = 3; STATE_CAB_RUN_CMD = 4; STATE_CAB_CLEAR_MSG = 5; var CabBus_RS485_Select : sbit; sfr; external; CabBus_RS485_Select_Direction : sbit; sfr; external; var RS485_Watchdog: Byte; ProxyNode: TNodeInfo; GlobalTimer: Word; implementation // ***************************************************************************** // Called from the UART RX Interrupt // The Pin Change Interrupt stopped the Timeout Timer so we are free to handle // the UART RX at our leasure // ***************************************************************************** procedure CabBus_UART_RX_StateMachine; begin while (URXDA_U2STA_bit = 1) do begin CabBridge.IncomingStarted := True; CabBridge.IncomingBuffer[CabBridge.iIncomingCount] := U2RXREG; Inc(CabBridge.iIncomingCount); if CabBridge.iIncomingCount = 2 then CabBridge.iStateMachine := STATE_SUB_BRIDGE_CAB_REPLIED // Received both Bytes process them end; end; // ***************************************************************************** // Called from Dynamically set Interrupt timer after 1800us // ***************************************************************************** procedure CabBus_Timeout; begin // The Ping timed out without a reply from the Cab move to the next state (depends of which wait state we are in) DisableCabBusTimer; if CabBridge.iStateMachine = STATE_SUB_BRIDGE_WAIT_SYNC_DELAY then CabBridge.iStateMachine := STATE_SUB_BRIDGE_INITIAIZE_PING else begin if RIDLE_U2STA_bit = 0 then Exit; if CabBridge.IncomingStarted then Exit; if URXDA_U2STA_bit = 1 then Exit; if CabBridge.iStateMachine = STATE_SUB_BRIDGE_WAIT_FOR_RESPONSE then CabBridge.iStateMachine := STATE_SUB_BRIDGE_TIMEOUT; end; end; // ***************************************************************************** // ***************************************************************************** function HandleCabBusReply(CabNode: PNMRAnetNode): Boolean; var CabData: PCab; i: Integer; begin Result := False; CabData := PCab( CabNode^.UserData); // If the current buffer in the Cab is not processed yet throw this one away as it may be in the middle of a statemachine if CabData^.UserInputBuffer.Full then Exit; // NCE interleaves its incoming messages (in some cases) so we need a sub-statemachine to // gather them up and then set the Cab Statemachine to the correct task. This is fast // from within the main Cab Ping loop. The handlers are all local to each nodes // statemachine loops for i := 0 to CabBridge.iIncomingCount - 1 do begin case CabBridge.IncomingBuffer[i] of NCE_NO_KEY_TO_REPORT : begin end; // Throw it away NCE_NO_SPEED_TO_REPORT : begin end; // Throw it away NCE_CAB_SELECT_MACRO : begin {$IFNDEF FPC}WriteByte(NCE_CMD_CURSOR_ON, True);{$ENDIF} CabData^.State := CabData^.State or CS_NCE_MACRO_MESSAGE; CabData^.UserInputBuffer.Count := 0; // Need to piecemealing together the full message the user it inputing end; NCE_CAB_SELECT_LOCO : begin {$IFNDEF FPC}WriteByte(NCE_CMD_CURSOR_ON, True);{$ENDIF} CabData^.State := CabData^.State or CS_NCE_LOCO_SELECT; CabData^.UserInputBuffer.Count := 0; // Need to piecemealing together the full message the user it inputing end; NCE_CAB_ENTER : begin if CabData^.State and (CS_NCE_MACRO_MESSAGE or CS_NCE_LOCO_SELECT) <> 0 then begin {$IFNDEF FPC}WriteByte(NCE_CMD_CURSOR_OFF, True);{$ENDIF} CabData^.UserInputBuffer.Full := True; Result := True; end; end else begin if CabData^.UserInputBuffer.Count < CAB_MAX_DATA_BYTE then begin CabData^.UserInputBuffer.DataBytes[CabData^.UserInputBuffer.Count] := CabBridge.IncomingBuffer[i]; Inc(CabData^.UserInputBuffer.Count); if CabData^.State and CS_NCE_MULTI_FRAME_MESSAGE = 0 then begin CabData^.UserInputBuffer.Full := True; Result := True; end; end; end; end; {case} end; {if} end; // ***************************************************************************** // ***************************************************************************** function DispatchMessage(CabNode: PNMRAnetNode): Boolean; var CabData: PCab; i: Integer; sss: array[0..128] of char; begin Result := False; if CabNode <> nil then begin CabData := PCab( CabNode^.UserData); {$IFDEF LOG_CAB_REPLY} UART1_Write_Text('--------'+LF); {$ENDIF} if CabData^.State and CS_NCE_LOCO_SELECT <> 0 then begin // Kick off the statemachine to do this.... CabNode^.iUserStateMachine := STATE_CAB_SELECT_LOCO; CabData^.iStateMachine := STATE_SUB_BRIDGE_INITIALIZE; {$IFDEF LOG_CAB_REPLY} UART1_Write_Text('Loco Select: '); {$ENDIF} end else if CabData^.State and CS_NCE_MACRO_MESSAGE <> 0 then begin // Kick off the statemachine to do this.... CabNode^.iUserStateMachine := STATE_CAB_RUN_MACRO; CabData^.iStateMachine := STATE_SUB_BRIDGE_INITIALIZE; {$IFDEF LOG_CAB_REPLY} UART1_Write_Text('Macro: '); {$ENDIF} end else begin CabNode^.iUserStateMachine := STATE_CAB_RUN_CMD; CabData^.iStateMachine := STATE_SUB_BRIDGE_INITIALIZE; {$IFDEF LOG_CAB_REPLY} UART1_Write_Text('Command: '); PrintIncomingMsg(CabData^.IncomingMsg); UART1_Write_Text('/--------'+LF); {$ENDIF} end; end; Result := True; Exit; end; // ***************************************************************************** // procedure UserStateMachine_Initialize // Parameters: : None // Returns : None // Description : Called once when the library is starting. Use to initalize // variables, etc // ***************************************************************************** procedure UserStateMachine_Initialize; begin NMRAnetCabBridge_Initialize; ProxyNode.ID[0] := 0; ProxyNode.ID[1] := 0; ProxyNode.AliasID := 0; GlobalTimer := 0; end; // ***************************************************************************** // procedure AppCallback_UserStateMachine_Process // Parameters: : None // Returns : None // Description : Called as often as possible to run the user statemachine // ***************************************************************************** procedure AppCallback_UserStateMachine_Process(Node: PNMRAnetNode); var i: Integer; CabData: PCab; Address, NewFunctionValue: Word; SpeedStep, AddressType: Byte; NewSpeed: THalfFloat; begin if Node = GetPhysicalNode then begin case Node^.iUserStateMachine of STATE_BRIDGE_USER_START : // Create the minimum number of Pings to put on the NCE bus to make it happy begin if Node^.State and NS_PERMITTED <> 0 then begin GlobalTimer := 0; if TrySendIdentifyProducer(Node^.Info, @EVENT_IS_PROXY) then Node^.iUserStateMachine := STATE_BRIDGE_FIND_PROXY; end; Exit; end; STATE_BRIDGE_FIND_PROXY : // Find the Proxy node (Command Station) on the network before progressing begin if (ProxyNode.AliasID > 0) or (ProxyNode.ID[0] > 0) or (ProxyNode.ID[1] > 0) then Node^.iUserStateMachine := STATE_BRIDGE_CREATE_REQUIRED_CABS else begin if GlobalTimer > 1 then Node^.iUserStateMachine := STATE_BRIDGE_USER_START // Try again end; Exit; end; STATE_BRIDGE_CREATE_REQUIRED_CABS : begin CabBridge.Discovering := True; for i := ID_MIN_DEVICE_NCE to ID_MIN_DEVICE_NCE + NCE_CAB_BUS_PADDING - 1 do CreateCab(i); // Build and assign cabs 2 - N so make the throttle hardware happy Node^.iUserStateMachine := STATE_BRIDGE_POLL_CABS; Exit; end; STATE_BRIDGE_POLL_CABS : begin case CabBridge.iStateMachine of // I could use the Train Data machine for this if I wanted STATE_SUB_BRIDGE_INITIALIZE : begin {$IFDEF DEBUG_DISCOVER_STATEMACHINE} UART1_Write_Text('STATE_DISCOVER_INITIALIZE'+LF); {$ENDIF} LATB4_bit := 1; FlushUartReceiver; if (CabBridge.iAssignedCabCount = 0) or (CabBridge.DiscoverTimer >= REDISCOVERY_TIME) then begin CabBridge.Discovering := True; CabBridge.DiscoverTimer := 0; end; if CabBridge.Discovering then CabBridge.CurrentCabID := ID_MIN_DEVICE_NCE else begin CabBridge.iAssignedCab := 0; CabBridge.CurrentCabID := PCab( CabBridge.AssignedCabs[0]^.UserData)^.ID; // One must exist if we get to here, see above test end; CabBridge.iIncomingCount := 0; CabBridge.iIncomingByteIndex := 0; CabBridge.iOutGoingCount := 1; CabBridge.iOutGoingByteIndex := 0; CabBridge.OutGoingBuffer[0] := NCEBUS_PING or 0; // Setup outgoing message (Sync) CabBridge.iStateMachine := STATE_SUB_BRIDGE_SYNC_TRANSMIT_BYTE; /// STATE_SUB_BRIDGE_SYNC_WAITFOR_HARDWARE_BUFFER_SPACE; Exit; end; STATE_SUB_BRIDGE_SYNC_TRANSMIT_BYTE : begin {$IFDEF DEBUG_DISCOVER_STATEMACHINE} UART1_Write_Text('STATE_SUB_BRIDGE_SYNC_TRANSMIT_BYTE..............'+LF); {$ENDIF} LATB4_bit := 0; // Start the transmission by setting the IC to Transmit if CabBridge.iOutGoingByteIndex = 0 then CabBus_RS485_Select := 1; // Select the 485 chip to transmit mode // Pump in as many bytes to the hardware buffers as possible while (CabBridge.iOutGoingByteIndex < CabBridge.iOutGoingCount) and (UTXBF_U2STA_bit = 0) do begin U2TXREG := CabBridge.OutGoingBuffer[CabBridge.iOutGoingByteIndex]; Inc(CabBridge.iOutGoingByteIndex); end; if CabBridge.iOutGoingByteIndex >= CabBridge.iOutGoingCount then begin // Special case if we are sending the last byte. Need to setup the interrupt to catch the last byte out U2TXIF_bit := 0; U2TXIE_bit := 1; CabBridge.iStateMachine := STATE_SUB_BRIDGE_SYNC_WAITFOR_HARDWARE_BUFFER_EMPTY; // Wait until the last byte is transmitted end; Exit; end; STATE_SUB_BRIDGE_SYNC_WAITFOR_HARDWARE_BUFFER_EMPTY : begin if (U2TXIF_bit = 1) then // The last byte was sent setup the wait period (plus or minus) begin {$IFDEF DEBUG_DISCOVER_STATEMACHINE} UART1_Write_Text('STATE_SUB_BRIDGE_SYNC_WAITFOR_HARDWARE_BUFFER_EMPTY..............'+LF); {$ENDIF} FlushUartReceiver; // Make sure the Timer isn't tricked into not ending the delay CabBridge.IncomingStarted := False; EnableCabBusTimer(28800); // 31.25ns * 28800 = 900us Need delay, the cabs are slow microprocessor CabBridge.iStateMachine := STATE_SUB_BRIDGE_WAIT_SYNC_DELAY; end; Exit; end; STATE_SUB_BRIDGE_WAIT_SYNC_DELAY : begin {$IFDEF DEBUG_DISCOVER_STATEMACHINE} UART1_Write_Text('STATE_DISCOVER_WAIT_SYNC_DELAY'+LF); {$ENDIF} Exit; // Waiting for the Timer to Expire and jump us to the next state end; STATE_SUB_BRIDGE_INITIAIZE_PING : begin {$IFDEF DEBUG_DISCOVER_STATEMACHINE} UART1_Write_Text('STATE_SUB_BRIDGE_INITIAIZE_PING'+LF); {$ENDIF} // Need a final test to make sure any existing Cab Nodes have fully // booted or have been taken off line before pinging them // ExistingCab is used further down so don't modify it CabBridge.ExistingCab := FindCab(CabBridge.CurrentCabID); // this may return null when discovering!!!! if CabNodeAssignedAndPermitted(CabBridge.ExistingCab) then begin FlushUartReceiver; CabBridge.iIncomingCount := 0; CabBridge.iIncomingByteIndex := 0; CabBridge.iOutGoingCount := 1; CabBridge.iOutGoingByteIndex := 0; CabBridge.OutGoingBuffer[0] := NCEBUS_PING or CabBridge.CurrentCabID; // Setup outgoing message CabBridge.LastPortRead := PortB; CNIE_bit := 1; // Pin Change Interrupt enable CabBus_RS485_Select := 1; // Select the 485 chip to transmit mode CabBridge.iStateMachine := STATE_SUB_BRIDGE_PING_TRANSMIT_BYTE; end else CabBridge.iStateMachine := STATE_SUB_BRIDGE_NEXT_CAB; Exit; end; STATE_SUB_BRIDGE_PING_TRANSMIT_BYTE : begin {$IFDEF DEBUG_DISCOVER_STATEMACHINE} UART1_Write_Text('STATE_SUB_BRIDGE_PING_TRANSMIT_BYTE..............'+LF); {$ENDIF} // Start the transmission by setting the IC to Transmit if CabBridge.iOutGoingByteIndex = 0 then CabBus_RS485_Select := 1; // Select the 485 chip to transmit mode // Pump in as many bytes to the hardware buffers as possible while (CabBridge.iOutGoingByteIndex < CabBridge.iOutGoingCount) and (UTXBF_U2STA_bit = 0) do begin U2TXREG := CabBridge.OutGoingBuffer[CabBridge.iOutGoingByteIndex]; Inc(CabBridge.iOutGoingByteIndex); end; if CabBridge.iOutGoingByteIndex >= CabBridge.iOutGoingCount then begin // Special case if we are sending the last byte. Need to setup the interrupt to catch the last byte out U2TXIF_bit := 0; U2TXIE_bit := 1; CabBridge.iIncomingStateMachine := 0; CabBridge.iStateMachine := STATE_SUB_BRIDGE_PING_WAITFOR_HARDWARE_BUFFER_EMPTY; // Wait until the last byte is transmitted end; Exit; end; STATE_SUB_BRIDGE_PING_WAITFOR_HARDWARE_BUFFER_EMPTY : begin {$IFDEF DEBUG_DISCOVER_STATEMACHINE} UART1_Write_Text('STATE_SUB_BRIDGE_PING_WAITFOR_HARDWARE_BUFFER_EMPTY..............'+LF); {$ENDIF} if (U2TXIF_bit = 1) then // The last byte was sent setup the wait period (plus or minus) begin CabBridge.IncomingStarted := False; RS485_Watchdog := 0; EnableCabBusTimer(38400); // 31.25ns * 38400 = 1200us Need delay, the cabs are slow microprocessors CabBridge.iStateMachine := STATE_SUB_BRIDGE_WAIT_FOR_RESPONSE; end; Exit; end; STATE_SUB_BRIDGE_WAIT_FOR_RESPONSE : begin // Waiting for the Timer to expire or we detect a reply.... // See CabBus_Timeout function for the Timeout or CabBus_UART_RX_StateMachine for a reply and what the next state is {$IFDEF DEBUG_DISCOVER_STATEMACHINE} UART1_Write_Text('STATE_DISCOVER_WAIT_FOR_RESPONSE'+LF); {$ENDIF} if RS485_Watchdog > 2 then // Something is corrupted or hung begin CabBridge.iIncomingCount := 0; CabBridge.iStateMachine := STATE_SUB_BRIDGE_TIMEOUT; end; Exit; end; STATE_SUB_BRIDGE_TIMEOUT : begin {$IFDEF DEBUG_DISCOVER_STATEMACHINE} UART1_Write_Text('STATE_DISCOVER_TIMEOUT'+LF); {$ENDIF} // Need to search the Allocated Cab List to find the CabBridge.iDiscoveryCabID matching Node to pull this CabBridge.iStateMachine := STATE_SUB_BRIDGE_NEXT_CAB; Exit; end; STATE_SUB_BRIDGE_CAB_REPLIED : begin {$IFDEF DEBUG_DISCOVER_STATEMACHINE} UART1_Write_Text('STATE_DISCOVER_CAB_REPLIED'+LF); {$ENDIF} if CabBridge.ExistingCab = nil then // Loaded earlier to test if an existing node was Permited before pinging begin CreateCab(CabBridge.CurrentCabID); // Throw away this message as it could be corrupted depending on timing of when the cab was plugged in CabBridge.iStateMachine := STATE_SUB_BRIDGE_NEXT_CAB end else begin if HandleCabBusReply(CabBridge.ExistingCab) then CabBridge.iStateMachine := STATE_SUB_BRIDGE_DISPATCH_MESSAGE else CabBridge.iStateMachine := STATE_SUB_BRIDGE_NEXT_CAB end; Exit; end; STATE_SUB_BRIDGE_DISPATCH_MESSAGE : begin {$IFDEF DEBUG_DISCOVER_STATEMACHINE} UART1_Write_Text('STATE_SUB_BRIDGE_DISPATCH_MESSAGE'+LF); {$ENDIF} if DispatchMessage(CabBridge.ExistingCab) then CabBridge.iStateMachine := STATE_SUB_BRIDGE_NEXT_CAB; Exit; end; STATE_SUB_BRIDGE_NEXT_CAB : begin {$IFDEF DEBUG_DISCOVER_STATEMACHINE} UART1_Write_Text('STATE_DISCOVER_NEXT_CAB'+LF); {$ENDIF} if CabBridge.Discovering then begin Inc(CabBridge.CurrentCabID); if CabBridge.CurrentCabID > ID_MAX_DEVICE_NCE then begin CabBridge.Discovering := False; CabBridge.DiscoverTimer := 0; CabBridge.iStateMachine := STATE_SUB_BRIDGE_INITIALIZE; // Start over and send the Sync again end else CabBridge.iStateMachine := STATE_SUB_BRIDGE_INITIAIZE_PING; // Ping the next one end else begin Inc(CabBridge.iAssignedCab); if CabBridge.iAssignedCab >= CabBridge.iAssignedCabCount then CabBridge.iStateMachine := STATE_SUB_BRIDGE_INITIALIZE // Start over and send the Sync again else begin CabBridge.CurrentCabID := PCab( CabBridge.AssignedCabs[CabBridge.iAssignedCab]^.UserData)^.ID; // One must exist if we get to here, see above test CabBridge.iStateMachine := STATE_SUB_BRIDGE_INITIAIZE_PING; // Ping the next known one end; end; Exit; end; end; Exit; end; end; end else begin // Cab Nodes, These states are entered by key presses and set by the Physical // node's interaction with the Cab Bus CabData := PCab( Node^.UserData); case Node^.iUserStateMachine of STATE_BRIDGE_USER_START : begin if Node^.State and NS_PERMITTED <> 0 then Node^.iUserStateMachine := STATE_CAB_IDLE; Exit; end; STATE_CAB_IDLE : begin // Nothing going on.... Exit; end; STATE_CAB_SELECT_LOCO : begin case CabData^.iStateMachine of STATE_SUB_BRIDGE_INITIALIZE : begin {$IFDEF DEBUG_STATE_CAB_SELECT_LOCO_STATEMACHINE} UART1_Write_Text('STATE_SUB_BRIDGE_INITIALIZE'+LF); {$ENDIF} CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_SEND_PROXY_MANAGE_LOCK; Exit; end; STATE_CAB_SELECT_LOCO_SEND_PROXY_MANAGE_LOCK : begin {$IFDEF DEBUG_STATE_CAB_SELECT_LOCO_STATEMACHINE} UART1_Write_Text('STATE_CAB_SELECT_LOCO_SEND_PROXY_MANAGE_LOCK'+LF); {$ENDIF} if TrySendTractionProxyManage(Node^.Info, ProxyNode, True) then CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_GENERIC_REPLY_WAIT; // Wait for the Manage Reply Callback CabData^.WatchDog_1s := 0; Exit; end; STATE_CAB_SELECT_LOCO_SEND_PROXY_ALLOCATE : begin {$IFDEF DEBUG_STATE_CAB_SELECT_LOCO_STATEMACHINE} UART1_Write_Text('STATE_CAB_SELECT_LOCO_SEND_PROXY_ALLOCATE'+LF); {$ENDIF} Address := NCE_CabMessageToTrainAddress(CabData^.UserInputBuffer.Count, @CabData^.UserInputBuffer.DataBytes); {$IFNDEF FPC} WordToStr(Address, s1); UART1_Write_Text('Address = ' + s1 + LF); {$ENDIF} SpeedStep := 28; { AppCallback_ReadConfiguration(CONFIG_OFFSET_SPEED_STEP, 1, @SpeedStep); // These offsets are into the Physical Nodes configuration address space AppCallback_ReadConfiguration(CONFIG_OFFSET_ADDRESS_TYPE, 1, @AddressType); if AddressType = 1 then Address := Address or $C000; case SpeedStep of 0 : SpeedStep := 14; 1 : SpeedStep := 28; 2 : SpeedStep := 128; end; } if TrySendTractionProxyAllocate(Node^.Info, ProxyNode, TRACTION_PROXY_TECH_ID_DCC, Address, SpeedStep, 0) then CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_GENERIC_REPLY_WAIT; // Wait for the Allocate Reply Callback CabData^.WatchDog_1s := 0; Exit; end; STATE_CAB_SELECT_LOCO_SEND_PROXY_MANAGE_UNLOCK : begin {$IFDEF DEBUG_STATE_CAB_SELECT_LOCO_STATEMACHINE} UART1_Write_Text('STATE_CAB_SELECT_LOCO_SEND_PROXY_MANAGE_UNLOCK'+LF); {$ENDIF} if TrySendTractionProxyManage(Node^.Info, ProxyNode, False) then CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_SEND_TRACTION_MANAGE_LOCK; // No Reply for Unlock CabData^.WatchDog_1s := 0; Exit; end; STATE_CAB_SELECT_LOCO_SEND_TRACTION_MANAGE_LOCK : begin {$IFDEF DEBUG_STATE_CAB_SELECT_LOCO_STATEMACHINE} UART1_Write_Text('STATE_CAB_SELECT_LOCO_SEND_TRACTION_MANAGE_LOCK'+LF); {$ENDIF} if TrySendTractionManage(Node^.Info, Node^.TrainData.LinkedNode, True) then CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_GENERIC_REPLY_WAIT; // Wait for the Lock Reply Callback CabData^.WatchDog_1s := 0; Exit; end; STATE_CAB_SELECT_LOCO_SEND_TRACTION_ASSIGN_CONTROLLER : begin {$IFDEF DEBUG_STATE_CAB_SELECT_LOCO_STATEMACHINE} UART1_Write_Text('STATE_CAB_SELECT_LOCO_SEND_TRACTION_ASSIGN_CONTROLLER'+LF); {$ENDIF} if TrySendTractionControllerConfig(Node^.Info, Node^.TrainData.LinkedNode, Node^.Info, True) then CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_GENERIC_REPLY_WAIT; // Wait for the Lock Reply Callback CabData^.WatchDog_1s := 0; Exit; end; STATE_CAB_SELECT_LOCO_SEND_TRACTION_QUERY_SPEED : begin {$IFDEF DEBUG_STATE_CAB_SELECT_LOCO_STATEMACHINE} UART1_Write_Text('STATE_CAB_SELECT_LOCO_SEND_TRACTION_QUERY_SPEED'+LF); {$ENDIF} if TrySendTractionQuerySpeed(Node^.Info, Node^.TrainData.LinkedNode) then begin CabData^.iQueryFunction := 0; CabData^.QueryType := QUERY_FOR_ALLOCATION; CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_GENERIC_REPLY_WAIT; // Wait for the Lock Reply Callback end; CabData^.WatchDog_1s := 0; Exit; end; STATE_CAB_SELECT_LOCO_SEND_TRACTION_QUERY_FUNCTIONS : begin {$IFDEF DEBUG_STATE_CAB_SELECT_LOCO_STATEMACHINE} UART1_Write_Text('STATE_CAB_SELECT_LOCO_SEND_TRACTION_QUERY_FUNCTIONS'+LF); {$ENDIF} if TrySendTractionQueryFunction(Node^.Info, Node^.TrainData.LinkedNode, CabData^.iQueryFunction) then CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_GENERIC_REPLY_WAIT; // Wait for the Lock Reply Callback CabData^.WatchDog_1s := 0; Exit; end; STATE_CAB_SELECT_LOCO_SEND_TRACTION_MANAGE_UNLOCK : begin {$IFDEF DEBUG_STATE_CAB_SELECT_LOCO_STATEMACHINE} UART1_Write_Text('STATE_CAB_SELECT_LOCO_SEND_TRACTION_MANAGE_UNLOCK'+LF); {$ENDIF} if TrySendTractionManage(Node^.Info, Node^.TrainData.LinkedNode, False) then Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... CabData^.WatchDog_1s := 0; Exit; end; STATE_CAB_SELECT_LOCO_GENERIC_REPLY_WAIT : begin // Waiting for the Reply to come into a callback if CabData^.WatchDog_1s > TIMEOUT_MESSAGE_REPLY_WAIT then begin {$IFDEF DEBUG_STATE_CAB_SELECT_LOCO_STATEMACHINE} UART1_Write_Text('STATE_CAB_SELECT_LOCO_SEND_PROXY_MANAGE_REPLY_WAIT'+LF); {$ENDIF} CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_GENERIC_TIMEOUT_PROXY_UNLOCK; // Force unlocks and exit end; Exit; end; STATE_CAB_SELECT_LOCO_GENERIC_TIMEOUT_PROXY_UNLOCK : begin {$IFDEF DEBUG_STATE_CAB_SELECT_LOCO_STATEMACHINE} UART1_Write_Text('STATE_CAB_SELECT_LOCO_GENERIC_TIMEOUT_PROXY_UNLOCK'+LF); {$ENDIF} // Unsure if we are locked or not, just release just in case if TrySendTractionProxyManage(Node^.Info, ProxyNode, False) then CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_SEND_TRACTION_MANAGE_UNLOCK; // No Reply for Unlock, just unlock the Traction Protcol and end Exit; end; end; end; STATE_CAB_RUN_MACRO : begin if NMRAnetUtilities_NullNodeIDInfo(Node^.TrainData.LinkedNode) then begin Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... Exit; end else begin Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... Exit; end; end; STATE_CAB_RUN_CMD : begin if NMRAnetUtilities_NullNodeIDInfo(Node^.TrainData.LinkedNode) then begin Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... Exit; end else begin case CabData^.UserInputBuffer.DataBytes[0] of NCE_CAB_DIR_TOGGLE : begin if Node^.TrainData.SpeedDir and $8000 <> 0 then Node^.TrainData.SpeedDir := Node^.TrainData.SpeedDir and not $8000 else Node^.TrainData.SpeedDir := Node^.TrainData.SpeedDir or $8000; if TrySendTractionSpeedSet(Node^.Info, Node^.TrainData.LinkedNode, Node^.TrainData.SpeedDir) then Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... Exit; end; NCE_HORN_KEY_DOWN : begin if TrySendTractionFunctionSet(Node^.Info, Node^.TrainData.LinkedNode, FUNCTION_HORN, 1) then Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... Exit; end; NCE_CAB_ONE_STEP_FASTER : begin NewSpeed := ChangeSpeed(Node^.TrainData.SpeedDir, +1); if TrySendTractionSpeedSet(Node^.Info, Node^.TrainData.LinkedNode, NewSpeed) then begin Node^.TrainData.SpeedDir := NewSpeed; Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... end; Exit; end; NCE_CAB_ONE_STEP_SLOWER : begin NewSpeed := ChangeSpeed(Node^.TrainData.SpeedDir, -1); if TrySendTractionSpeedSet(Node^.Info, Node^.TrainData.LinkedNode, NewSpeed) then begin Node^.TrainData.SpeedDir := NewSpeed; Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... end; Exit; end; NCE_CAB_EMERGENCY_STOP : begin if TrySendTractionEmergencyStop(Node^.Info, Node^.TrainData.LinkedNode) then Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... Exit; end; NCE_CAB_BELL : begin if ToggleFunction(Node, Node^.TrainData.Functions, FUNCTION_BELL, NewFunctionValue) then Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... Exit; end; NCE_CAB_TOGGLE_F0_0 : begin if ToggleFunction(Node, Node^.TrainData.Functions, 0, NewFunctionValue) then Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... end; NCE_CAB_TOGGLE_F1_1 : begin if ToggleFunction(Node, Node^.TrainData.Functions, 1, NewFunctionValue) then Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... Exit; end; NCE_CAB_TOGGLE_F2_2 : begin if ToggleFunction(Node, Node^.TrainData.Functions, 2, NewFunctionValue) then Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... Exit; end; NCE_CAB_TOGGLE_F3_3 : begin if ToggleFunction(Node, Node^.TrainData.Functions, 3, NewFunctionValue) then Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... Exit; end; NCE_CAB_TOGGLE_F4_4 : begin if ToggleFunction(Node, Node^.TrainData.Functions, 4, NewFunctionValue) then Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... Exit; end; NCE_CAB_TOGGLE_F5_5 : begin if ToggleFunction(Node, Node^.TrainData.Functions, 5, NewFunctionValue) then Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... Exit; end; NCE_CAB_TOGGLE_F6_6 : begin if ToggleFunction(Node, Node^.TrainData.Functions, 6, NewFunctionValue) then Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done... Exit; end; NCE_CAB_TOGGLE_F7_7 : begin if ToggleFunction(Node, Node^.TrainData.Functions, 7, NewFunctionValue) then Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... Exit; end; NCE_CAB_TOGGLE_F8_8 : begin if ToggleFunction(Node, Node^.TrainData.Functions, 8, NewFunctionValue) then Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... Exit; end; NCE_CAB_9 : begin if ToggleFunction(Node, Node^.TrainData.Functions, 9, NewFunctionValue) then Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... Exit; end; NCE_HORN_KEY_UP : begin if TrySendTractionFunctionSet(Node^.Info, Node^.TrainData.LinkedNode, FUNCTION_HORN, 0) then Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... Exit; end; NCE_CAB_FIVE_STEPS_FASTER : begin NewSpeed := ChangeSpeed(Node^.TrainData.SpeedDir, +5); if TrySendTractionSpeedSet(Node^.Info, Node^.TrainData.LinkedNode, NewSpeed) then begin Node^.TrainData.SpeedDir := NewSpeed; Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... end; Exit; end; NCE_CAB_FIVE_STEPS_SLOWER : begin NewSpeed := ChangeSpeed(Node^.TrainData.SpeedDir, -5); if TrySendTractionSpeedSet(Node^.Info, Node^.TrainData.LinkedNode, NewSpeed) then begin Node^.TrainData.SpeedDir := NewSpeed; Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... end; Exit; end; NCE_CAB_DIR_FORWARD : begin if TrySendTractionDirectionSet(Node^.Info, Node^.TrainData.LinkedNode, Node^.TrainData.SpeedDir, True) then Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... Exit; end; NCE_CAB_DIR_REVERSE : begin if TrySendTractionDirectionSet(Node^.Info, Node^.TrainData.LinkedNode, Node^.TrainData.SpeedDir, False) then Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... Exit; end else Node^.iUserStateMachine := STATE_CAB_CLEAR_MSG; // We are done.... end; end; end; STATE_CAB_CLEAR_MSG : begin {$IFDEF DEBUG_STATE_CAB_STATEMACHINE} UART1_Write_Text('STATE_CAB_CLEAR_MSG'+LF); {$ENDIF} CabData^.UserInputBuffer.Full := False; CabData^.State := CabData^.State and not CS_NCE_MULTI_FRAME_MESSAGE; CabData^.UserInputBuffer.Count := 0; Node^.iUserStateMachine := STATE_CAB_IDLE; // We are done.... CabData^.iStateMachine := STATE_SUB_BRIDGE_INITIALIZE; // Reset end; end; end; end; // ***************************************************************************** // procedure AppCallback_NodeInitialize // Parameters: : Node : Pointer to the node that needs to be initilized to its intial value // Returns : None // Description : Typically called when a node is being intialized to be // logged into the network. It is possible the node can be // discarded then reused so it may be called more than once for // virtual nodes // ***************************************************************************** procedure AppCallback_NodeInitialize(Node: PNMRAnetNode); begin // Assign the user data record to the Node for future use Node^.UserData := @CabArray[Node^.iIndex]; Node^.iUserStateMachine := STATE_BRIDGE_USER_START; // Initialize the data, every time the node is reused! ZeroizeNceCabData( PCab (Node^.UserData)) end; {$IFDEF SUPPORT_TRACTION} // ***************************************************************************** // procedure AppCallback_TractionControlReply // Parameters: : Source : Full Node ID (and Alias if on CAN) of the source node for the message // Dest : Full Node ID (and Alias if on CAN) of the dest node for the message // DataBytes: pointer to the raw data bytes // Returns : None // Description : Called when a Traction Protocol request comes in // ***************************************************************************** procedure AppCallback_TractionProtocol(Node: PNMRAnetNode; AMessage: POPStackMessage); var MultiFrameBuffer: PMultiFrameBuffer; begin {$IFDEF FPC}EnterCriticalsection(OPStackCriticalSection);{$ENDIF} MultiFrameBuffer := PMultiFrameBuffer( PByte( AMessage^.Buffer)); case MultiFrameBuffer^.DataArray[0] of TRACTION_CONTROLLER_CONFIG : begin case MultiFrameBuffer^.DataArray[1] of TRACTION_CONTROLLER_CONFIG_NOTIFY : begin end; end; end; end; {$IFDEF FPC}LeaveCriticalsection(OPStackCriticalSection);{$ENDIF} end; // ***************************************************************************** // procedure AppCallback_TractionProtocolReply // Parameters: : Node : Pointer to the node that the traction protocol has been called on // ReplyMessage : The Reply Message that needs to be allocated, populated and returned so it can be sent // RequestingMessage : Message that was sent to the node containing the requested information // Returns : True if the RequestingMessage is handled and the ReplyMessage is ready to send // False if the request has not been completed due to no available buffers or waiting on other information // Description : Called in response to a Traction Protcool request // ***************************************************************************** procedure AppCallback_TractionProtocolReply(Node: PNMRAnetNode; AMessage: POPStackMessage); var MultiFrameBuffer: PMultiFrameBuffer; CabData: PCab; FunctionAddress, Mask: DWord; FunctionValue: Word; begin MultiFrameBuffer := PMultiFrameBuffer( PByte( AMessage^.Buffer)); CabData := PCab( Node^.UserData); case MultiFrameBuffer^.DataArray[0] of TRACTION_QUERY_SPEED : begin Node^.TrainData.SpeedDir := (MultiFrameBuffer^.DataArray[1] shl 8) or MultiFrameBuffer^.DataArray[2]; CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_SEND_TRACTION_QUERY_FUNCTIONS end; TRACTION_QUERY_FUNCTION : begin FunctionAddress := DWord(MultiFrameBuffer^.DataArray[1] shr 16) or DWord(MultiFrameBuffer^.DataArray[2] shr 8) or DWord(MultiFrameBuffer^.DataArray[3]); FunctionValue := Word(MultiFrameBuffer^.DataArray[4] shr 8) or Word(MultiFrameBuffer^.DataArray[5]); Mask := $00000001; Mask := Mask shl FunctionAddress; if FunctionValue = 0 then Node^.TrainData.Functions := Node^.TrainData.Functions and not Mask else Node^.TrainData.Functions := Node^.TrainData.Functions or Mask; if CabData^.QueryType = QUERY_FOR_ALLOCATION then begin if PCab( Node^.UserData)^.iQueryFunction < 28 then begin CabData^.iQueryFunction := FunctionAddress + 1; CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_SEND_TRACTION_QUERY_FUNCTIONS; end else CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_SEND_TRACTION_MANAGE_UNLOCK end else if CabData^.QueryType = QUERY_NORMAL then begin CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_SEND_TRACTION_MANAGE_UNLOCK end else CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_SEND_TRACTION_MANAGE_UNLOCK end; TRACTION_CONTROLLER_CONFIG : begin case MultiFrameBuffer^.DataArray[1] of TRACTION_CONTROLLER_CONFIG_ASSIGN : begin if MultiFrameBuffer^.DataArray[2] = TRACTION_CONTROLLER_ASSIGN_REPLY_OK then CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_SEND_TRACTION_QUERY_SPEED else CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_GENERIC_TIMEOUT_PROXY_UNLOCK // Can't reserve now go back to normal polling end; end; end; TRACTION_CONSIST : begin case MultiFrameBuffer^.DataArray[1] of TRACTION_CONSIST_ATTACH : begin end; TRACTION_CONSIST_DETACH : begin end; TRACTION_CONSIST_QUERY : begin end; end // case end; TRACTION_MANAGE : begin case MultiFrameBuffer^.DataArray[1] of TRACTION_MANAGE_RESERVE : begin if MultiFrameBuffer^.DataArray[2] = TRACTION_MANAGE_RESERVE_REPLY_OK then CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_SEND_TRACTION_ASSIGN_CONTROLLER else CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_GENERIC_TIMEOUT_PROXY_UNLOCK // Can't reserve now go back to normal polling end; end end; end; end; {$ENDIF} {$IFDEF SUPPORT_TRACTION_PROXY} // ***************************************************************************** // procedure AppCallback_TractionProtocol // Parameters: : Node : Pointer to the node that the traction protocol has been called on // ReplyMessage : The Reply Message that needs to be allocated, populated and returned so it can be sent // RequestingMessage : Message that was sent to the node containing the requested information // Returns : True if the RequestingMessage is handled and the ReplyMessage is ready to send // False if the request has not been completed due to no available buffers or waiting on other information // Description : Called when a Traction Protocol message is received // ***************************************************************************** function AppCallback_TractionProxyProtocol(Node: PNMRAnetNode; AMessage: POPStackMessage; SourceHasLock: Boolean): Boolean; begin Result := False; end; // ***************************************************************************** // procedure AppCallback_TractionProxyProtocolReply // Parameters: : Source : Full Node ID (and Alias if on CAN) of the source node for the message // Dest : Full Node ID (and Alias if on CAN) of the dest node for the message // DataBytes: pointer to the raw data bytes // Returns : None // Description : Called in response to a Traction Proxy request // ***************************************************************************** procedure AppCallback_TractionProxyProtocolReply(Node: PNMRAnetNode; AMessage: POPStackMessage); var MultiFrameBuffer: PMultiFrameBuffer; CabData: PCab; i: Integer; begin MultiFrameBuffer := PMultiFrameBuffer( PByte(AMessage^.Buffer)); CabData := PCab( Node^.UserData); case AMessage^.Buffer^.DataArray[0] of TRACTION_PROXY_MANAGE : begin if AMessage^.Buffer^.DataArray[1] = TRACTION_PROXY_MANAGE_RESERVE then begin if AMessage^.Buffer^.DataArray[2] = 0 then CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_SEND_PROXY_ALLOCATE // Move to next state after reserving else CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_GENERIC_TIMEOUT_PROXY_UNLOCK // Can't reserve now go back to normal polling end; Exit; end; TRACTION_PROXY_ALLOCATE : begin Node^.TrainData.LinkedNode.AliasID := (MultiFrameBuffer^.DataArray[11] shl 8) or (MultiFrameBuffer^.DataArray[12]); NMRAnetUtilities_Load48BitNodeIDWithSimpleData(Node^.TrainData.LinkedNode.ID, PSimpleDataArray( PByte( @MultiFrameBuffer^.DataArray[5]))^); CabData^.iStateMachine := STATE_CAB_SELECT_LOCO_SEND_PROXY_MANAGE_UNLOCK; // Now need to unlock the Proxy Exit; end; end; // case end; {$ENDIF} // ***************************************************************************** // procedure AppCallBack_ProtocolSupportReply // Parameters: : Source : Full Node ID (and Alias if on CAN) of the source node for the message // Dest : Full Node ID (and Alias if on CAN) of the dest node for the message // DataBytes: pointer Raw data bytes, Byte 0 and 1 are the Alias // Returns : None // Description : Called in response to a Protocol Support Request // ***************************************************************************** procedure AppCallBack_ProtocolSupportReply(Node: PNMRAnetNode; AMessage: POPStackMessage); begin end; // ***************************************************************************** // procedure AppCallback_ConsumerIdentified // Parameters: : Source : Full Node ID (and Alias if on CAN) of the source node for the message // Dest : Full Node ID (and Alias if on CAN) of the dest node for the message // MTI : MTI of the message // EventID: pointer to the Event ID for the message // Returns : None // Description : This is called directly from the Hardware receive buffer. Do // not do anything here that stalls the call. This is called // Asyncronously from the Statemachine loop and the Statemachine loop // is stalled until this returns. Set a flag and move on is the // best stratagy or store info in a buffer and process in the // main statemachine. // ***************************************************************************** procedure AppCallback_ConsumerIdentified(var Source: TNodeInfo; MTI: Word; EventID: PEventID); begin end; // ***************************************************************************** // procedure AppCallback_ProducerIdentified // Parameters: : Source : Full Node ID (and Alias if on CAN) of the source node for the message // Dest : Full Node ID (and Alias if on CAN) of the dest node for the message // MTI : MTI of the message // EventID: pointer to the Event ID for the message // Returns : None // Description : This is called directly from the Hardware receive buffer. Do // not do anything here that stalls the call. This is called // Asyncronously from the Statemachine loop and the Statemachine loop // is stalled until this returns. Set a flag and move on is the // best stratagy or store info in a buffer and process in the // main statemachine. // ***************************************************************************** procedure AppCallback_ProducerIdentified(var Source: TNodeInfo; MTI: Word; EventID: PEventID); begin if NMRAnetUtilities_EqualEventID(EventID, @EVENT_IS_PROXY) then ProxyNode := Source; end; // ***************************************************************************** // procedure AppCallback_LearnEvent // Parameters: : Source : Full Node ID (and Alias if on CAN) of the source node for the message // EventID: pointer to the Event ID for the message // Returns : None // Description : This is called directly from the Hardware receive buffer. Do // not do anything here that stalls the call. This is called // Asyncronously from the Statemachine loop and the Statemachine loop // is stalled until this returns. Set a flag and move on is the // best stratagy or store info in a buffer and process in the // main statemachine. // ***************************************************************************** procedure AppCallback_LearnEvent(var Source: TNodeInfo; EventID: PEventID); begin end; // ***************************************************************************** // procedure AppCallBack_PCEventReport // Parameters: : Source : Full Node ID (and Alias if on CAN) of the source node for the message // EventID: pointer to the Event ID for the message // Returns : None // Description : This is called directly from the Hardware receive buffer. Do // not do anything here that stalls the call. This is called // Asyncronously from the Statemachine loop and the Statemachine loop // is stalled until this returns. Set a flag and move on is the // best stratagy or store info in a buffer and process in the // main statemachine. // ***************************************************************************** procedure AppCallBack_PCEventReport(var Source: TNodeInfo; EventID: PEventID); begin end; // ***************************************************************************** // procedure AppCallBack_ConfigMemReadReply // Parameters: : Node : Pointer to the node that needs to be initilized to its intial value // Returns : None // Description : Typically called when a node is being intialized to be // logged into the network. It is possible the node can be // discarded then reused so it may be called more than once for // virtual nodes // ***************************************************************************** procedure AppCallBack_ConfigMemReadReply(Node: PNMRAnetNode; AMessage: POPStackMessage; Success: Boolean); begin end; // ***************************************************************************** // procedure AppCallBack_ConfigMemStreamReadReply // Parameters: : Node : Pointer to the node that needs to be initilized to its intial value // Returns : None // Description : Typically called when a node is being intialized to be // logged into the network. It is possible the node can be // discarded then reused so it may be called more than once for // virtual nodes // ***************************************************************************** procedure AppCallBack_ConfigMemStreamReadReply(Node: PNMRAnetNode; AMessage: POPStackMessage; Success: Boolean); begin end; // ***************************************************************************** // procedure AppCallBack_ConfigMemWriteReply // Parameters: : Node : Pointer to the node that needs to be initilized to its intial value // Returns : None // Description : Typically called when a node is being intialized to be // logged into the network. It is possible the node can be // discarded then reused so it may be called more than once for // virtual nodes // ***************************************************************************** procedure AppCallBack_ConfigMemWriteReply(Node: PNMRAnetNode; AMessage: POPStackMessage; Success: Boolean); begin end; // ***************************************************************************** // procedure AppCallBack_ConfigMemStreamWriteReply // Parameters: : Node : Pointer to the node that needs to be initilized to its intial value // Returns : None // Description : Typically called when a node is being intialized to be // logged into the network. It is possible the node can be // discarded then reused so it may be called more than once for // virtual nodes // ***************************************************************************** procedure AppCallBack_ConfigMemStreamWriteReply(Node: PNMRAnetNode; AMessage: POPStackMessage; Success: Boolean); begin end; // ***************************************************************************** // procedure AppCallback_RemoteButtonReply // Parameters: : Source : Full Node ID (and Alias if on CAN) of the source node for the message // Dest : Full Node ID (and Alias if on CAN) of the dest node for the message // DataBytes: pointer to the raw data bytes // Returns : None // Description : Called in response to a Remote Button request // ***************************************************************************** procedure AppCallback_RemoteButtonReply(Node: PNMRAnetNode; var Source: TNodeInfo; DataBytes: PSimpleBuffer); begin end; {$IFDEF SUPPORT_TRACTION} // ***************************************************************************** // procedure AppCallback_SimpleTrainNodeInfoReply // Parameters: : Source : Full Node ID (and Alias if on CAN) of the source node for the message // Dest : Full Node ID (and Alias if on CAN) of the dest node for the message // TrainNodeInfo: pointer to the null terminated strings // Returns : None // Description : Called in response to a STNIP request // ***************************************************************************** procedure AppCallback_SimpleTrainNodeInfoReply(Node: PNMRAnetNode; AMessage: POPStackMessage); begin end; {$ENDIF} // ***************************************************************************** // procedure AppCallback_Timer_1s // Parameters: : None // Returns : None // Description : Typcally called from another thread or interrupt, only use // to update asyncronous flags // ***************************************************************************** procedure AppCallback_Timer_1s; var i: Integer; Cab: PCab; begin Inc(GlobalTimer); Inc(RS485_Watchdog); // Count up to the time out then freeze. The Timer Count will be reset after the // main loop is done rediscovering if CabBridge.DiscoverTimer < REDISCOVERY_TIME then Inc(CabBridge.DiscoverTimer); for i := 0 to CabBridge.iAssignedCabCount - 1 do begin Cab := PCab( CabBridge.AssignedCabs[i]^.UserData); Inc( Cab^.WatchDog_1s); end; end; // ***************************************************************************** // procedure AppCallback_SimpleNodeInfoReply // Parameters: : Source : Full Node ID (and Alias if on CAN) of the source node for the message // Dest : Full Node ID (and Alias if on CAN) of the dest node for the message // NodeInfo : pointer to the null terminated strings // Returns : None // Description : Called in response to a SNIP Request // ***************************************************************************** procedure AppCallback_SimpleNodeInfoReply(Node: PNMRAnetNode; AMessage: POPStackMessage); begin end; // ***************************************************************************** // procedure AppCallback_VerifiedNodeID // Parameters: : Source : Full Node ID (and Alias if on CAN) of the source node for the message // EventID: pointer to the Event ID for the message // Returns : None // Description : This is called directly from the Hardware receive buffer. Do // not do anything here that stalls the call. This is called // Asyncronously from the Statemachine loop and the Statemachine loop // is stalled until this returns. Set a flag and move on is the // best stratagy or store info in a buffer and process in the // main statemachine. // ***************************************************************************** procedure AppCallback_VerifiedNodeID(var Source: TNodeInfo; NodeID: PNodeID); begin end; // ***************************************************************************** // procedure AppCallback_InitializationComplete // Parameters: : Source : Full Node ID (and Alias if on CAN) of the source node for the message // EventID: pointer to the Event ID for the message // Returns : None // Description : This is called directly from the Hardware receive buffer. Do // not do anything here that stalls the call. This is called // Asyncronously from the Statemachine loop and the Statemachine loop // is stalled until this returns. Set a flag and move on is the // best stratagy or store info in a buffer and process in the // main statemachine. // ***************************************************************************** procedure AppCallback_InitializationComplete(var Source: TNodeInfo; NodeID: PNodeID); begin end; end.