unit NMRAnetAppCallbacks; // ****************************************************************************** // // * 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 // // * Revision History: // 2012-02-01: Created // // * Description: // Implements program specific callbacks for memory configuration etc. // // AppCallbacks for the Mustangpeak Command Station // // ****************************************************************************** {$I Options.inc} uses _25AAxxxx, NMRAnetStateMachine, NMRAnetDCC, CANStorage, NMRAnetAppDefines, NMRAnetDefines, NMRAnetNode, NMRAnetUtilities, Float16, NMRAnetDefinesShared; const TRACTION_PROTOCOL_MASK = $F0; TRACTION_OLCB = $00; TRACTION_DCC = $80; TRACTION_OP_MASK = $0F; TRACTION_OP_SPEED_DIR = $00; TRACTION_OP_FUNCTION = $01; TRACTION_OP_E_STOP = $02; TRACTION_OP_PROXY_MGMT = $02; DCC_ALLOCATE_ADDRESS = $01; DCC_DEALLOCATE_ADDRESS = $02; DCC_FUNCTION_28 = $00; DCC_FUNCTION_32k = $01; const _28_STEP_TABLE: array[0..28] of Byte = ( %00000000, // Stop %00000010, // Step 1 %00010010, // Step 2 %00000011, // Step 3 %00010011, // Step 4 %00000100, // Step 5 %00010100, // Step 6 %00000101, // Step 7 %00010101, // Step 8 %00000110, // Step 9 %00010110, // Step 10 %00000111, // Step 11 %00010111, // Step 12 %00001000, // Step 13 %00011000, // Step 14 %00001001, // Step 15 %00011001, // Step 16 %00001010, // Step 17 %00011010, // Step 18 %00001011, // Step 19 %00011011, // Step 20 %00001100, // Step 21 %00011100, // Step 22 %00001101, // Step 23 %00011101, // Step 24 %00001110, // Step 25 %00011110, // Step 26 %00001111, // Step 27 %00011111 // Step 28 ); _14_STEP_TABLE: array[0..14] of Byte = ( %00000000, // Stop %00000010, // Step 1 %00000011, // Step 3 %00000100, // Step 5 %00000101, // Step 7 %00000110, // Step 9 %00000111, // Step 11 %00001000, // Step 13 %00001001, // Step 15 %00001010, // Step 17 %00001011, // Step 19 %00001100, // Step 21 %00001101, // Step 23 %00001110, // Step 25 %00001111 // Step 27 ); const PS_DCC_ADDRESS_ALLOCATED = $01; // The Proxy is Allocated MSG_SEND_DCC_ADDRESS_ALLOCATED = $01; // Need to send a Message for the DCC Address was Allocated type TDccProxyData = record // Structure stored in RAM on a node and vnode basis State: Byte; // State of the Proxy Node Speed: Word; // Float 16 so negative is reverse (including -0) SpeedSteps: Byte; // TEMPORARY - PUT IN CONFIGURATION Functions: DWord; // Function State (F0..F28) Address: Word; // DCC Address end; PDccProxyData = ^TDccProxyData; const MAX_NODE_CFG_DATA = 128; // This allows the EEPROM to never get out of sync and need to be erased and rebuilt, just grow TDCCConfigurationData up to 1024 MAX_VNODE_CFG_DATA = 2304; // This allows the EEPROM to never get out of sync and need to be erased and rebuilt, just grow TDCCConfigurationData up to 1024 type TDCCConfigurationData = record ConsistNext, // // 4 Bytes ConsistPrev: TNodeID; // // 4 Bytes end; PDCCConfigurationData = ^TDCCConfigurationData; procedure NMRAnetAppCallbacks_Initialize; procedure AppCallback_AssignRAMAddress(Node: PNMRANetNode; iNode: Word); function AppCallback_ProducerIdentify(Node: PNMRAnetNode; Event: PEventID): Boolean; function AppCallback_ConsumerIdentify(Node: PNMRAnetNode; Event: PEventID): Boolean; function AppCallback_EventsIdentify: Boolean; function AppCallback_EventsIdentifyByDest(Node: PNMRAnetNode): Boolean; procedure AppCallback_TractionControl(Node: PNMRAnetNode; CANBuffer: PCANBuffer); function AppCallback_StateMachine(Node: PNMRAnetNode; CANBuffer: PCANBuffer; DataBytesPtr: PCAN_DataBytes): Boolean; function AppCallback_UserMessageFlags(Node: PNMRAnetNode; CANBuffer: PCANBuffer; DataBytesPtr: PCAN_DataBytes): Boolean; procedure AppCallback_NodeAllocate(Node: PNMRAnetNode); // Address Space Support // ***************************************************************************** procedure AppCallback_AssignConfigurationAddress(Node: PNMRANetNode; iNode: Word); function AppCallback_AddressSpacePresent(Node: PNMRAnetNode; AddressSpace: Byte): Boolean; function AppCallback_AddressSpaceReadOnly(Node: PNMRAnetNode; AddressSpace: Byte): Boolean; function AppCallback_Configuration_AddressSpaceSize(Node: PNMRAnetNode): Word; function AppCallback_Configuration_Read(Node: PNMRAnetNode; DataTarget: ^Byte; StartAddress: DWord; MaxCount: Byte): Byte; procedure AppCallback_Configuration_Write(Node: PNMRAnetNode; DataTarget: ^Byte; StartAddress: DWord; MaxCount: Byte); function AppCallback_FDI_AddressSpaceSize(Node: PNMRAnetNode): Word; procedure AppCallback_FDI_Write(Node: PNMRAnetNode; DataTarget: ^Byte; StartAddress: DWord; MaxCount: Byte); function AppCallback_FDI_Read(Node: PNMRAnetNode; DataTarget: ^Byte; StartAddress: DWord; MaxCount: Byte): Byte; // ***************************************************************************** // Local to Train Nodes function GetProxyData(Node: PNMRAnetNode): PDccProxyData; implementation // Array of records that contain Volatile Data (RAM only) for each Node. The address of the record is assigned to the // node in the AssignRAMAddress function below var VolatileData: array[0..MAX_NODE_COUNT - 1] of TDccProxyData; procedure UpdateFunctionsAndSendDCCMessage(AddressHi, AddressLo, FunctionAddress, FunctionValue: Byte; var DCCPacket: TDCCPacket; ProxyData: PDccProxyData); var WideFunctionMask: DWord; FunctionMask, FunctionExtendedCode: Byte; begin WideFunctionMask := $00000001; WideFunctionMask := WideFunctionMask shl FunctionAddress; // Set the correct Function Bit ProxyData^.Functions := ProxyData^.Functions and not WideFunctionMask; // Clear the bit if FunctionValue > 0 then ProxyData^.Functions := ProxyData^.Functions or WideFunctionMask; // Set the bit if needed if FunctionAddress < 29 then begin if FunctionAddress < 5 then begin FunctionMask := (ProxyData^.Functions shr 1) and $0F; FunctionMask.4 := ProxyData^.Functions.0; FunctionMask := FunctionMask or %10000000; // Opcode bits end else if FunctionAddress < 9 then begin FunctionMask := (ProxyData^.Functions shr 5) and $0F; FunctionMask := FunctionMask or %10110000; // Opcode bits end else if FunctionAddress < 13 then begin FunctionMask := (ProxyData^.Functions shr 9) and $0F; FunctionMask := FunctionMask or %10100000; // Opcode bits end else if FunctionAddress < 21 then begin FunctionMask := ProxyData^.Functions shr 13; FunctionExtendedCode := %11011110 end end else begin FunctionMask := ProxyData^.Functions shr 21; FunctionExtendedCode := %11011111 end; // Now create the DCC Packet if AddressHi and $C0 = $C0 then begin // UART1_Write_Text('Long Address'+LF); if FunctionAddress < 13 then NMRA_DCC_LoadPacket(@DCCPacket, AddressHi, AddressLo, FunctionMask, 0, 0, 3) else NMRA_DCC_LoadPacket(@DCCPacket, AddressHi, AddressLo, FunctionExtendedCode, FunctionMask, 0, 4) end else begin if FunctionAddress < 13 then NMRA_DCC_LoadPacket(@DCCPacket, AddressLo, FunctionMask, 0, 0, 0, 3) else NMRA_DCC_LoadPacket(@DCCPacket, AddressLo, FunctionExtendedCode, FunctionMask, 0, 0, 4) end; NMRA_DCC_QueuePacket(@Track, @DCCPacket, False); end; function GetProxyData(Node: PNMRAnetNode): PDccProxyData; begin Result := nil; if Node <> nil then Result := PDccProxyData( Node^.RAMAddress); end; // ***************************************************************************** // procedure NMRAnetNode_FindFirstNonAllocatedVirtualNode; // // Parameters: // // Result: // // Description: // READ ME: // Currently this uses a dog slow search. // ***************************************************************************** function FindFirstNonDCCAllocatedNode: PNMRAnetNode; var i: Integer; DCCProxyData: PDccProxyData; begin Result := nil; i := Nodes.AllocatedCount - 1; while (i > -1) do begin if NMRAnetNode_TestStateFlag(Nodes.AllocatedList[i], NS_VIRTUAL) then begin DCCProxyData := GetProxyData(Nodes.AllocatedList[i]); if DCCProxyData^.State and PS_DCC_ADDRESS_ALLOCATED = 0 then begin Result := Nodes.AllocatedList[i]; Break; end end; Dec(i); end; end; // ***************************************************************************** // Returns The Is Present Attribute about the Address Space // Node: The Node to write the Configuration Data for // AddressSpace: The Address Space to return the information about // // Result : True if Is Present else False // ***************************************************************************** function AppCallback_AddressSpacePresent(Node: PNMRAnetNode; AddressSpace: Byte): Boolean; begin if NMRAnetNode_TestStateFlag(Node, NS_VIRTUAL) then Result := (AddressSpace <= MSI_CDI) and (AddressSpace >= MSI_FDI) else Result := (AddressSpace <= MSI_CDI) and (AddressSpace >= MSI_ACDI_USER) end; // ***************************************************************************** // Returns The Is ReadOnly Attribute about the Address Space // Node: The Node to write the Configuration Data for // AddressSpace: The Address Space to return the information about // // Result : True if Is ReadOnly else False // ***************************************************************************** function AppCallback_AddressSpaceReadOnly(Node: PNMRAnetNode; AddressSpace: Byte): Boolean; begin case AddressSpace of MSI_CDI, MSI_ALL, MSI_ACDI_MFG, MSI_ACDI_USER, // I am not supporting writing to this space, do it through the configuration addresss space MSI_FDI : Result := True else Result := False; end; end; //****************************************************************************** // Associates the offset of the EEPROM for the Configuration Data for the Node //****************************************************************************** procedure AppCallback_AssignConfigurationAddress(Node: PNMRANetNode; iNode: Word); begin // The first node is the Physical Node so this math works, iNode = 0 is the Physical node and al vNodes > 0 for iNode if NMRAnetNode_TestStateFlag(Node, NS_VIRTUAL) then Node^.ConfigurationAddress := Generic32BitPointer( MAX_NODE_CFG_DATA + ((iNode - 1) * MAX_VNODE_CFG_DATA)) else Node^.ConfigurationAddress := Generic32BitPointer( 0) ; end; //****************************************************************************** // Returns the overall size of the Configuration Data //****************************************************************************** function AppCallback_Configuration_AddressSpaceSize(Node: PNMRAnetNode): Word; begin if NMRAnetNode_TestStateFlag(Node, NS_VIRTUAL) then Result := MAX_VNODE_CFG_DATA else Result := MAX_NODE_CFG_DATA end; // ***************************************************************************** // Writes the Configuration Memory from the App defined storage device // Node: The Node to write the Configuration Data for // DataTarget : A pointer memory space to read the Configuration Data to, this could be a Datagram, CAN Bytes, etc. The Length defines how much to write // StartAddress: Offset from 0x00 in the Configuration Memory to start from // MaxCount : Maximum number of Bytes to move into the EEPROM // // Result : Number of Bytes actually moved in to the DataTarget // ***************************************************************************** procedure AppCallback_Configuration_Write(Node: PNMRAnetNode; DataTarget: ^Byte; StartAddress: DWord; MaxCount: Byte); begin while _25AAxxxx_Busy(EEPROM_BANK_0) do Delay_us(10); _25AAxxxx_Write(EEPROM_BANK_0, Node^.ConfigurationAddress + StartAddress, MaxCount, DataTarget); end; // ***************************************************************************** // Reads the Configuration Memory from the App defined storage device // Node: The Node to read the Configuration Data for // DataTarget : A pointer memory space to write the Configuration Data to, this could be a Datagram, CAN Bytes, etc. The Length defines how much to write // StartAddress: Offset from 0x00 in the Configuration Memory to start from // MaxCount : Maximum number of Bytes to move into the DataTarget // // Result : Number of Bytes actually moved in to the DataTarget // ***************************************************************************** function AppCallback_Configuration_Read(Node: PNMRAnetNode; DataTarget: ^Byte; StartAddress: DWord; MaxCount: Byte): Byte; begin while _25AAxxxx_Busy(EEPROM_BANK_0) do Delay_us(10); Result := MaxCount; _25AAxxxx_Read(EEPROM_BANK_0, Node^.ConfigurationAddress + StartAddress, MaxCount, DataTarget); end; function AppCallback_FDI_AddressSpaceSize(Node: PNMRAnetNode): Word; begin if NMRAnetNode_TestStateFlag(Node, NS_VIRTUAL) then Result := MAX_FDI_ADDRESS_SPACE_SIZE else Result := 0; end; procedure AppCallback_FDI_Write(Node: PNMRAnetNode; DataTarget: ^Byte; StartAddress: DWord; MaxCount: Byte); begin while _25AAxxxx_Busy(EEPROM_BANK_0) do Delay_us(10); _25AAxxxx_Write(EEPROM_BANK_0, Node^.ConfigurationAddress + StartAddress + OFFSET_FDI_DATA, MaxCount, DataTarget); end; function AppCallback_FDI_Read(Node: PNMRAnetNode; DataTarget: ^Byte; StartAddress: DWord; MaxCount: Byte): Byte; var i: Integer; begin while _25AAxxxx_Busy(EEPROM_BANK_0) do Delay_us(10); Result := MaxCount; _25AAxxxx_Read(EEPROM_BANK_0, Node^.ConfigurationAddress + StartAddress + OFFSET_FDI_DATA, MaxCount, DataTarget); end; //****************************************************************************** // Associates the Address of the RAM for Volatile Node Data //****************************************************************************** procedure AppCallback_AssignRAMAddress(Node: PNMRANetNode; iNode: Word); begin Node^.RAMAddress := Generic32BitPointer( @VolatileData[iNode]); end; //****************************************************************************** // Initialize the VolatileData structure //****************************************************************************** procedure NMRAnetAppCallbacks_Initialize; var i: Integer; begin for i := 0 to MAX_NODE_COUNT - 1 do begin VolatileData[i].State := 0; VolatileData[i].Speed := 0; VolatileData[i].Functions := 0; VolatileData[i].MsgFlags := 0; VolatileData[i].Address := 0; VolatileData[i].SpeedSteps := 28; // Default end end; procedure AppCallback_NodeAllocate(Node: PNMRAnetNode); var ProxyData: PDccProxyData; begin ProxyData := GetProxyData(Node); ProxyData^.State := 0; ProxyData^.Address := 0; ProxyData^.MsgFlags := 0; ProxyData^.Speed := 0; ProxyData^.Functions := 0; end; // ***************************************************************************** // // ***************************************************************************** procedure SetProxyNodeDccAddressFlags(Node: PNMRAnetNode; Event: PEventID); var TestForSetFlag: Boolean; ProxyData: PDccProxyData; Address: Word; begin TestForSetFlag := True; if Event <> nil then TestForSetFlag := (Event^[0] = $06) and (Event^[1] = $01); if TestForSetFlag then begin ProxyData := GetProxyData(Node); if ProxyData^.State and PS_DCC_ADDRESS_ALLOCATED = PS_DCC_ADDRESS_ALLOCATED then begin Address := (Event^[4] shl 8) or Event^[5]; if ProxyData^.Address = Address then ProxyData^.MsgFlags := ProxyData^.MsgFlags or MSG_SEND_DCC_ADDRESS_ALLOCATED; end end end; // ***************************************************************************** // // ***************************************************************************** procedure SetProxyNodeProducerFlags(Node: PNMRAnetNode; EventIndex: Integer); begin // This uses the fact that there are hard coded will known Event ID offsets in the Event Arrays case EventIndex of EVENT_TRAIN_INDEX : NMRAnetNode_SetProducerEventFlag(Node, EventIndex, EVENT_STATE_VALID); EVENT_TRAIN_DCC_IDLE_INDEX : begin if GetProxyData(Node)^.State and PS_DCC_ADDRESS_ALLOCATED = PS_DCC_ADDRESS_ALLOCATED then NMRAnetNode_SetProducerEventFlag(Node, EventIndex, EVENT_STATE_INVALID) else NMRAnetNode_SetProducerEventFlag(Node, EventIndex, EVENT_STATE_VALID); end; EVENT_TRAIN_DCC_INUSE_INDEX : begin if GetProxyData(Node)^.State and PS_DCC_ADDRESS_ALLOCATED = PS_DCC_ADDRESS_ALLOCATED then NMRAnetNode_SetProducerEventFlag(Node, EventIndex, EVENT_STATE_VALID) else NMRAnetNode_SetProducerEventFlag(Node, EventIndex, EVENT_STATE_INVALID); end else NMRAnetNode_SetProducerEventFlag(Node, EventIndex, EVENT_STATE_UNKOWN); end end; // ***************************************************************************** // // ***************************************************************************** function AppCallback_ProducerIdentify(Node: PNMRAnetNode; Event: PEventID): Boolean; var i, VNodeEventIndex, NodeEventIndex: Integer; VNodeEvent, NodeEvent: Boolean; begin Result := True; // We handle the message VNodeEventIndex := -1; NodeEventIndex := -1; VNodeEvent := NMRAnetUtilities_SupportsVNodeEventAsProducer(Event, VNodeEventIndex); NodeEvent := NMRAnetUtilities_SupportsEventAsProducer(Event, NodeEventIndex); for i := 0 to Nodes.AllocatedCount - 1 do begin if NMRAnetNode_TestStateFlag(Nodes.AllocatedList[i], NS_VIRTUAL) then begin if VNodeEvent then SetProxyNodeProducerFlags(Nodes.AllocatedList[i], VNodeEventIndex); SetProxyNodeDccAddressFlags(Nodes.AllocatedList[i], Event); end else begin if NodeEvent then NMRAnetNode_SetProducerEventFlag(Nodes.AllocatedList[0], NodeEventIndex, EVENT_STATE_UNKOWN); end end; end; // ***************************************************************************** // // ***************************************************************************** function AppCallback_ConsumerIdentify(Node: PNMRAnetNode; Event: PEventID): Boolean; begin Result := False; // Do the default end; function AppCallback_EventsIdentify: Boolean; var j, ProducerIndex: Integer; Node: PNMRAnetNode; begin Result := True; // We handled it for j := 0 to Nodes.AllocatedCount - 1 do begin Node := Nodes.AllocatedList[j]; if NMRAnetNode_TestStateFlag(Node, NS_VIRTUAL) then begin NMRAnetNode_SetConsumerEventFlags(Node, EVENT_STATE_UNKOWN); // Consumers are eaay. for ProducerIndex := 0 to MAX_VNODE_SUPPORTED_EVENTS_PRODUCED - 1 do // Producers take some work begin SetProxyNodeProducerFlags(Node, ProducerIndex); SetProxyNodeDccAddressFlags(Node, nil); end end else begin NMRAnetNode_SetProducerEventFlags(Node, EVENT_STATE_UNKOWN); NMRAnetNode_SetConsumerEventFlags(Node, EVENT_STATE_UNKOWN); end end; end; // ***************************************************************************** // // ***************************************************************************** function AppCallback_EventsIdentifyByDest(Node: PNMRAnetNode): Boolean; var ProducerIndex: Integer; begin Result := True; // We handled it NMRAnetNode_SetConsumerEventFlags(Node, EVENT_STATE_UNKOWN); // Consumers are eaay. if NMRAnetNode_TestStateFlag(Node, NS_VIRTUAL) then begin for ProducerIndex := 0 to MAX_VNODE_SUPPORTED_EVENTS_PRODUCED - 1 do // Producers take some work SetProxyNodeProducerFlags(Node, ProducerIndex); SetProxyNodeDccAddressFlags(Node, nil); end else begin NMRAnetNode_SetProducerEventFlags(Node, EVENT_STATE_UNKOWN); NMRAnetNode_SetConsumerEventFlags(Node, EVENT_STATE_UNKOWN); end; end; // ***************************************************************************** // // ***************************************************************************** procedure AppCallback_TractionControl(Node: PNMRAnetNode; CANBuffer: PCANBuffer); var ProxyData: PDccProxyData; FunctionAddress: DWord; FunctionValue: Word; AbsoluteSpeed: real; SpeedStep: Word; DCCPacket: TDCCPacket; AddressHi, AddressLo: Byte; IsForward: Boolean; begin ProxyData := GetProxyData(Node); AddressHi := (ProxyData^.Address shr 8) and $00FF; // Split the address to make clear when loading bytes AddressLo := ProxyDAta^.Address and $00FF; case CANBuffer^.DataBytes[2] and TRACTION_PROTOCOL_MASK of TRACTION_OLCB : begin // OLCB Train Protocol case CANBuffer^.DataBytes[2] and TRACTION_OP_MASK of TRACTION_OP_SPEED_DIR : begin ProxyData^.Speed := (CANBuffer^.DataBytes[3] shl 8) or (CANBuffer^.DataBytes[4]); IsForward := ProxyData^.Speed and $8000 <> $8000; AbsoluteSpeed := HalfToFloat(ProxyData^.Speed and not $8000); case ProxyData^.SpeedSteps of 14 : begin AbsoluteSpeed := (14/100) * AbsoluteSpeed; SpeedStep := Word( AbsoluteSpeed); SpeedStep := _14_STEP_TABLE[SpeedStep]; if IsForward then SpeedStep := SpeedStep or $60 else SpeedStep := SpeedStep or $40; if AddressHi and $C0 = $C0 then NMRA_DCC_LoadPacket(@DCCPacket, AddressHi, AddressLo, SpeedStep, 0, 0, 3) else NMRA_DCC_LoadPacket(@DCCPacket, AddressLo, SpeedStep, 0, 0, 0, 2); NMRA_DCC_QueuePacket(@Track, @DCCPacket, False); end; 28 : begin AbsoluteSpeed := (28/100) * AbsoluteSpeed; SpeedStep := Word( AbsoluteSpeed); SpeedStep := _28_STEP_TABLE[SpeedStep]; if IsForward then SpeedStep := SpeedStep or $60 else SpeedStep := SpeedStep or $40; if AddressHi and $C0 = $C0 then NMRA_DCC_LoadPacket(@DCCPacket, AddressHi, AddressLo, SpeedStep, 0, 0, 3) else NMRA_DCC_LoadPacket(@DCCPacket, AddressLo, SpeedStep, 0, 0, 0, 2); NMRA_DCC_QueuePacket(@Track, @DCCPacket, False); end; 128 : begin if AddressHi and $C0 = $C0 then begin AbsoluteSpeed := (127/100) * AbsoluteSpeed; SpeedStep := Word( AbsoluteSpeed); if SpeedStep > 0 then Inc(SpeedStep); // 1 = EStop if IsForward then SpeedStep := SpeedStep or $80; NMRA_DCC_LoadPacket(@DCCPacket, AddressHi, AddressLo, %00111111, SpeedStep, 0, 4); NMRA_DCC_QueuePacket(@Track, @DCCPacket, False); end; end; end; end; TRACTION_OP_E_STOP : begin IsForward := ProxyData^.Speed and $8000 <> $8000; if IsForward then ProxyData^.Speed := $0000 else ProxyData^.Speed := $8000; case ProxyData^.SpeedSteps of 14, 28 : begin SpeedStep := $01; if IsForward then SpeedStep := SpeedStep or $60 else SpeedStep := SpeedStep or $40; if AddressHi and $C0 = $C0 then NMRA_DCC_LoadPacket(@DCCPacket, AddressHi, AddressLo, SpeedStep, 0, 0, 3) else NMRA_DCC_LoadPacket(@DCCPacket, AddressLo, SpeedStep, 0, 0, 0, 2); NMRA_DCC_QueuePacket(@Track, @DCCPacket, False); end; 128 : begin SpeedStep := $01; if IsForward then SpeedStep := SpeedStep or $80; NMRA_DCC_LoadPacket(@DCCPacket, AddressHi, AddressLo, %00111111, SpeedStep, 0, 4); NMRA_DCC_QueuePacket(@Track, @DCCPacket, False); end; end end; TRACTION_OP_FUNCTION : begin FunctionAddress := (DWord( CANBuffer^.DataBytes[3]) shl 16) or (DWord( CANBuffer^.DataBytes[4]) shl 8) or DWord( CANBuffer^.DataBytes[5]); FunctionValue := (DWord( CANBuffer^.DataBytes[6]) shl 8) or DWord( CANBuffer^.DataBytes[7]); UpdateFunctionsAndSendDCCMessage(AddressHi, AddressLo, Byte( FunctionAddress), Byte( FunctionValue), DCCPacket, ProxyData); end; end; end; TRACTION_DCC : begin // DCC Train Protocol case CANBuffer^.DataBytes[2] and $0F of { TRACTION_OP_SPEED_DIR : begin ProxyData^.Speed := CANBuffer^.DataBytes[3]; if AddressHi and $C0 = $C0 then begin // Extended Address if CANBuffer^.DataBytes[4] = 128 then NMRA_DCC_LoadPacket(@DCCPacket, AddressHi, AddressLo, %00111111, ProxyData^.Speed, 0, 4) else NMRA_DCC_LoadPacket(@DCCPacket, AddressHi, AddressLo, ProxyData^.Speed, 0, 0, 3); NMRA_DCC_QueuePacket(@Track, @DCCPacket, False); end else begin // Short Address if CANBuffer^.DataBytes[4] < 128 then // Can't do short address with 128 Step begin NMRA_DCC_LoadPacket(@DCCPacket, AddressLo, ProxyData^.Speed, 0, 0, 0, 2); NMRA_DCC_QueuePacket(@Track, @DCCPacket, False); end end; end; TRACTION_OP_FUNCTION : begin FunctionAddress := (CANBuffer^.DataBytes[4] shl 8) or CANBuffer^.DataBytes[5]; FunctionValue := CANBuffer^.DataBytes[6]; case CANBuffer^.DataBytes[3] of DCC_FUNCTION_28 : UpdateFunctionsAndSendDCCMessage(AddressHi, AddressLo, Byte( FunctionAddress), Byte( FunctionValue), DCCPacket, ProxyData); DCC_FUNCTION_32k : begin end; // UART1_Write_Text('DCC_FUNCTION_32k'+LF); end; end; } TRACTION_OP_PROXY_MGMT : begin case CANBuffer^.DataBytes[3] of DCC_ALLOCATE_ADDRESS : begin ProxyData^.State := ProxyData^.State or PS_DCC_ADDRESS_ALLOCATED; ProxyData^.MsgFlags := ProxyData^.MsgFlags or MSG_SEND_DCC_ADDRESS_ALLOCATED; // Changed State so notify the system SetProxyNodeProducerFlags(Node, EVENT_TRAIN_DCC_IDLE_INDEX); // Changed State so notify the system SetProxyNodeProducerFlags(Node, EVENT_TRAIN_DCC_INUSE_INDEX); // Changed State so notify the system ProxyData^.SpeedSteps := CANBuffer^.DataBytes[4]; // TO DO: NEED TO WRITE TO CONFIGURATION ProxyData^.Address := Word ((CANBuffer^.DataBytes[5] shl 8)) or CANBuffer^.DataBytes[6]; // This is in NMRA DCC format for short/long address // if FindFirstNonDCCAllocatedNode = nil then // NMRAnetNode_Allocate; // Need a proxy that is free to allocate end; DCC_DEALLOCATE_ADDRESS : begin ProxyData^.State := ProxyData^.State and not PS_DCC_ADDRESS_ALLOCATED; ProxyData^.MsgFlags := ProxyData^.MsgFlags and not MSG_SEND_DCC_ADDRESS_ALLOCATED; // Changed State so notify the system SetProxyNodeProducerFlags(Node, EVENT_TRAIN_DCC_IDLE_INDEX); // Changed State so notify the system SetProxyNodeProducerFlags(Node, EVENT_TRAIN_DCC_INUSE_INDEX); // Changed State so notify the system end; end; end; end; end; end end; // ***************************************************************************** // Called if the User Message field is > 0 (Node.UserMsgFlags) // currently (2/13) does not allow overriding of the login or CAN layer messages // Node : The Node that has the timeslice to run its statemachine // CANBuffer : The raw data from the CAN receiver // DataBytesPtr : Pointer to a temporary CAN Byte array that can be used for any purpose // // Result : False to run the default handler, True if this callback handled the // processing and skip the default processing // ***************************************************************************** function AppCallback_UserMessageFlags(Node: PNMRAnetNode; CANBuffer: PCANBuffer; DataBytesPtr: PCAN_DataBytes): Boolean; begin // We are in a CAN Lock block already Result := False; // Handle the oddball DCC Address Event that can come and go if Node^.MsgUserFlags and MSG_SEND_DCC_ADDRESS_ALLOCATED = MSG_SEND_DCC_ADDRESS_ALLOCATED then begin DataBytesPtr^[0] := $06; DataBytesPtr^[1] := $01; DataBytesPtr^[2] := $00; DataBytesPtr^[3] := $00; DataBytesPtr^[4] := (ProxyData^.Address shr 8) and $00FF; DataBytesPtr^[5] := ProxyData^.Address and $00FF; DataBytesPtr^[6] := $00; DataBytesPtr^[7] := $01; if TransmitNMRABusLayerMsg(Node, CANBUffer, MTI_PRODUCER_IDENTIFIED_SET, 0, 8, DataBytesPtr, False) then begin Node^.MsgUserFlags := Node^.MsgUserFlags and not MSG_SEND_DCC_ADDRESS_ALLOCATED; Result := True; end end; end; function AppCallback_StateMachine(Node: PNMRAnetNode; CANBuffer: PCANBuffer; DataBytesPtr: PCAN_DataBytes): Boolean; begin Result := False end; end.