unit opstackdefines; {$IFDEF FPC} interface {$ENDIF} {$I Options.inc} uses {$IFDEF SUPPORT_TRACTION}Float16,{$ENDIF} template_node, opstacktypes, nmranetdefines, template_buffers; const LF = #13+#10; const TIMEOUT_MAX_BUS_LOGIN = 5; // Number of 100ms time tick to wait for a node to send a RID to signal a duplicate Alais TIMEOUT_ABANDON_RESOURCE = 5; // Number of seconds before a resource (message/buffer/etc) is delared abandon and needs to be released, must be greater than TIMEOUT_MESSAGE_REPLY_WAIT or you won't get the exptected results TIMEOUT_MESSAGE_REPLY_WAIT = 3; // Number of seconds before deciding the other node is not going to reply const STNIP_MAX_STR_LEN = 32; STNIP_OFFSET_ROADNAME = 128; // Need to move past the User Name and User Description space STNIP_OFFSET_CLASS = 160; STNIP_OFFSET_ROADNUMBER = 192; STNIP_OFFSET_TRAINNAME = 224; STNIP_OFFSET_MANUFACTURER = 256; STNIP_OFFSET_OWNER = 288; STNIP_OFFSET_TRAIN_ID = 320; STNIP_OFFSET_SHORT_LONG = 322; STNIP_OFFSET_SPEEDSTEPS = 323; STNIP_PROTOCOL: TPIVProtocolValueArray = ($00, $00, $80, $00, $00, $00); type TStnipBuffer = array[0..STNIP_MAX_STR_LEN] of Char; // Various Statemachine defines const STATE_NODE_START = 0; STATE_NODE_GENERATE_NODE_ALIAS = 1; STATE_RANDOM_NUMBER_GENERATOR = 2; STATE_NODE_TRANSMIT_CID = 3; STATE_NODE_NEXT_CDI = 4; STATE_NODE_WAITSTATE = 5; STATE_NODE_SEND_LOGIN_RID = 6; STATE_NODE_SEND_LOGIN_AMD = 8; STATE_NODE_INITIALIZED = 9; STATE_NODE_LOGIN_IDENTIFY_EVENTS = 10; STATE_NODE_PERMITTED = 11; STATE_NODE_INHIBITED = 12; STATE_NODE_DUPLICATE_FULL_ID = 13; STATE_NODE_TAKE_OFFLINE = 14; STATE_NODE_OFFLINE = 15; const MAX_ETHERNET_MESSAGE = 1500; // Nodes const MAX_SIMPLE_BYTES = 8; MAX_SNIP_BYTES = 64; MAX_DATAGRAM_BYTES = 72; // 64 Data bytes + 8 Bytes for DG Header USER_MAX_MULTI_FRAME_BYTES = 16; // type TNodeID = array[0..1] of DWORD; // WARNING READ THIS::::: The Bottom 3 Bytes = [0] and the Top 3 Bytes = [1] The ID is not continious across the both DWords the upper nibble of the bottom DWord is not used PNodeID = ^TNodeID; // = array[0..0] of Byte; // PDataArray = ^TDataArray; TSimpleDataArray = array[0..MAX_SIMPLE_BYTES-1] of Byte; PSimpleDataArray = ^TSimpleDataArray; TDatagramDataArray = array[0..MAX_DATAGRAM_BYTES-1] of Byte; PDatagramDataArray = ^TDatagramDataArray; TStreamDataArray = array[0..USER_MAX_STREAM_BYTES-1] of Byte; PStreamDataArray = ^TStreamDataArray; TMultiFrameStringDataArray = array[0..USER_MAX_MULTIFRAME_STRING_BYTES] of Byte; PAcdiSnipDataArray = ^TMultiFrameStringDataArray; TMultiFrameArray = array[0..USER_MAX_MULTI_FRAME_BYTES] of Byte; // This is common mulit-frame buffer for those pesky multi frame messages that are only a few frames long such as the Traction QuerySpeed, this gives us flexibility in lengthing it in the future if needed PMultiFrameArray = ^TMultiFrameArray; TSimpleData = record Count: Word; Bytes: TSimpleDataArray; end; TDatagramData = record Count: Word; Bytes: TDatagramDataArray; end; TStreamData = record Count: Word; Bytes: TStreamDataArray; end; // Events const NULL_NODE_ID: TNodeID = (0, 0); const EVENT_STATE_CLEAR = $00; EVENT_STATE_VALID = $01; EVENT_STATE_INVALID = $02; EVENT_STATE_UNKNOWN = $03; // Message types const MT_MASK = $00FF; // Strips off the CAN and Allocated flags MT_UNALLOCATED = $0000; MT_SIMPLE = $0001; // Message Type Identifiers MT_DATAGRAM = $0002; MT_STREAM = $0004; MT_ACDISNIP = $0008; MT_MULTIFRAME = $0010; MT_CAN_TYPE = $4000; // It is a CAN MTI MT_ALLOCATED = $8000; // Buffer was allocated from the Pool, do not set this manually !!!!! const // :X19170640N0501010107015555;#0 Example..... // ^ ^ ^ // 0 10 28 MAX_GRID_CONNECT_LEN = 29; GRID_CONNECT_HEADER_OFFSET_HI = 2; GRID_CONNECT_HEADER_OFFSET_LO = 4; GRID_CONNECT_DATA_OFFSET = 11; type {$IFDEF FPC} TGridConnectString = array[0..MAX_GRID_CONNECT_LEN-1] of ansichar; {$ELSE} TGridConnectString = array[0..MAX_GRID_CONNECT_LEN-1] of char; {$ENDIF} PGridConnectString = ^TGridConnectString; // ***************************************************************************** // Node // ***************************************************************************** const // NodeState the node empty and ready to allocate NS_EMPTY = $00; // NodeState the node is not allocated NS_ALLOCATED = $01; // NodeState the node is allocated NS_PERMITTED = $02; // NodeState CAN Frame Layer is permitted (Node ID's resolved with bus) NS_INITIALIZED = $04; // NodeState Message Layer has sent its first Initialize Complete Message {$IFDEF SUPPORT_VIRTUAL_NODES}NS_VIRTUAL = $08; {$ENDIF} // NodeState If is a virtual node NS_RELEASING = $10; // Node is tagged to send and AMD and be removed from the bus (while this is set what happens??) // MsgFlags in order of precidence (= 0 highest precidence) MF_DUPLICATE_NODE_ID = $0001; // MsgFlag, a Duplicate Node ID was detected, critical fault MF_DUPLICATE_ALIAS = $0002; // MsgFlag, a Duplicate Alias was Detected, critical fault MF_DUPLICATE_ALIAS_RID = $0004; // MsgFlag, a Duplicate Alias was Detected during a CID message, not a fault just need to respond to claim the Alias MF_ALIAS_MAP_ENQUIRY = $0008; // MsgFlag, an AMD message need to be responded to MF_VERIFY_NODE_ID = $0010; // MsgFlag, a Verify Node ID message needs to be responded to type // Each Byte contains the state of up to 4 Events, as each event can have 3 state (2 bits) // The Index of the 2 bits block is mapped to the index into the defined array of Event ID values TNodeEventStateArray = array[0..USER_MAX_EVENTS_BYTES] of Byte; // Holds the current state (set, clear, unknown) of each Event. Used when Consumer/Producer Indentifed messages need to be sent // Each Byte contains the state of up to 8 Events, as each event can have 1 state (1 bits) // The Index of the 1 bits block is mapped to the index into the defined array of Event ID values TNodeEventArray = array[0..USER_MAX_PCER_BYTES] of Byte; // This flags if the Event State should be sent // Each Byte contains the state of up to 8 Events, as each event can have 1 state (1 bits) // The Index of the 1 bits block is mapped to the index into the defined array of Event ID values TNodePCERArray = array[0..USER_MAX_PCER_BYTES] of Byte; // Holds a flag for if an Event requires a PCER message to be sent becuase that event has changed state TNodeInfo = record ID: TNodeID; // Unique 48 Bit ID for Node AliasID: Word; // 12 Bit Alias ID end; PNodeInfo = ^TNodeInfo; { const NULL_NODE_INFO: TNodeInfo = ( ID : (0, 0); AliasID: 0; ); } type TNMRAnetNodeLoginInfo = record TimeCounter_100ms: Byte; // Number of timer ticks into the time waiting for a RID response from another node for our RID broadcasts iCID: Byte; // Which of the 4 CIDs we are broadcasting Seed: TNodeID; // Seed for Random Number Generator in case we have to reseed because of a duplicate ID end; TNodeEvents = record ProducedState, ConsumedState : TNodeEventStateArray; Produced, Consumed : TNodeEventArray; // Flags if the Event message should be sent PCER : TNodePCERArray; // Flags if the PCER for the event should be sent end; const ABS_ALLOCATED = $01; // Array Buffer State Flag = Allocated Buffer type TSimpleBuffer = record State: Byte; // See ABS_xxxx flags DataBufferSize: Word; // Number of bytes in the DataBuffer DataArray: TSimpleDataArray; end; PSimpleBuffer = ^TSimpleBuffer; TDatagramBuffer = record State: Byte; // See ABS_xxxx flags DataBufferSize: Word; // Number of bytes in the DataArray DataArray: TDatagramDataArray; // ******* iStateMachine: Byte; CurrentCount: Word; // Current index of the number of bytes sent/received ResendCount: Byte; // Number of tries to resend the datagram if sending is rejected end; PDatagramBuffer = ^TDatagramBuffer; {$IFDEF SUPPORT_STREAMS} const MAX_STREAM_TYPE_ID = 6; type TStreamTypeID = array[0..MAX_STREAM_TYPE_ID-1] of Byte; PStreamTypeID = ^TStreamTypeID; TStreamBuffer = record State: Byte; // See ABS_xxxx flags DataBufferSize: Word; // Number of bytes in the DataArray that are valid, for streams this is the negotiated buffer size DataArray: TStreamDataArray; // ******* SourceStreamID, DestStreamID: Byte; StreamTypeID: TStreamTypeID; CurrentCount: DWord; // Current index of the number of bytes sent/received TotalMessageSize: DWord; // The total number of bytes to send in the interaction NegotiatedBufferSize: Word; NextActiveStream: PByte; end; PStreamBuffer = ^TStreamBuffer; {$ENDIF} TMultiFrameStringBuffer = record State: Byte; // See ABS_xxxx flags DataBufferSize: Word; // Number of bytes in the DataArray DataArray: TMultiFrameStringDataArray; // ******* CurrentCount: Word; // Current index of the number of bytes sent/received end; PMultiFrameStringBuffer = ^TMultiFrameStringBuffer; TMultiFrameBuffer = record State: Byte; // See ABS_xxxx flags DataBufferSize: Word; // Number of bytes in the DataArray DataArray: TMultiFrameArray; // ******* CurrentCount: Word; // Current index of the number of bytes sent/received end; PMultiFrameBuffer = ^TMultiFrameBuffer; type {$IFDEF FPC} POPStackMessage = ^TOPStackMessage; {$ENDIF} TOPStackMessage = record // Used as the "base class" for all the message records, allows this class to be overlayed the other to fake inheritance MessageType: Word; // MT_xxx Constant the identifies the type of message, bottom 4 bits are the type of message u Source: TNodeInfo; Dest: TNodeInfo; FramingBits: Byte; // The upper 4 bits sent in the Destination (when used for the message) {$IFDEF FPC} NextIncoming: POPStackMessage; NextOutgoing: POPStackMessage; {$ELSE} NextIncoming: ^TOPStackMessage; NextOutgoing: ^TOPStackMessage; {$ENDIF} MTI: Word; Buffer: PSimpleBuffer; // This can be nil, CANBuffer, Datagram Buffer, or StreamBuffer based on the lower 4 bits of MessageType WatchDog_1s: Word; // Watches for a abandon message, incremented every 1s end; {$IFNDEF FPC} POPStackMessage = ^TOPStackMessage; {$ENDIF} {$IFDEF SUPPORT_TRACTION} TTrainData = record State: Word; // Train State (see Train State (TS_xxxx) constants SpeedDir: THalfFloat; // Speed and direction (encoded in the sign) Functions: DWord; // F0..F28 Does this go in the configuration space? Address: Word; // DCC Address SpeedSteps: Byte; // 14, 28, 128 Does this go in the configuration space? Lock: TNodeInfo; // For the nodes lock managements ControllerLink, LinkedNode: TNodeInfo; // Controller (throttle) running the train or other linking needs Timer: Byte; end; PTrainData = ^TTrainData; {$ENDIF} type TNMRAnetNode = record iIndex: Byte; // Index in the main array State: Byte; // See the NS_xxxx flags; State of the Node Events: TNodeEvents; Info: TNodeInfo; // Information about a Node Login: TNMRAnetNodeLoginInfo; // Login Information Flags: Word; // Message Flags for messages passed to the Node through a simple set bit (no complex reply data needed like destination Alias), see the MF_xxxx flags iStateMachine: Byte; // Statemachine index for the main bus login IncomingMessages: POPStackMessage; // Linked List of Messages incoming to process for the node OutgoingMessages: POPStackMessage; // Linked List of Messages outgoing from the node, mainly for Datagrams and Streams {$IFDEF FPC} UserData: Pointer; {$ELSE} UserData: ^Byte; // Pointer to User Data {$ENDIF} iUserStateMachine: Byte; // For user (application level) statemachine UserWatchdog_1s: Word; // For user {$IFDEF SUPPORT_TRACTION}TrainData: TTrainData;{$ENDIF} // Realtime information about the Train Node end; PNMRAnetNode = ^TNMRAnetNode; type TNodePool = record Pool: array[0..USER_MAX_NODE_COUNT-1] of TNMRAnetNode; // Node [0] is ALWAYS the physical node AllocatedList: array[0..USER_MAX_NODE_COUNT-1] of PNMRAnetNode; // Node List sorted by Alias AllocatedCount: Integer; // Number of Nodes Allocated iActiveNode: Integer; // The node that is "active" which means it is the one that the main statemachine is giving a time slice to execute end; PNodePool = ^TNodePool; TOPStackEthernetMessage = array[0..MAX_ETHERNET_MESSAGE-1] of byte; POPStackEthernetMessage = ^TOPStackEthernetMessage; const DATAGRAM_PROCESS_ERROR_OK = $00; DATAGRAM_PROCESS_ERROR_BUFFER_FULL = $02; DATAGRAM_PROCESS_ERROR_OUT_OF_ORDER = $03; DATAGRAM_PROCESS_ERROR_SOURCE_NOT_ACCEPTED = $04; DATAGRAM_PROCESS_ERROR_QUIET_FAIL = $05; const STATE_DATAGRAM_PROCESS = 0; STATE_DATAGRAM_SEND_ACK = 1; STATE_DATAGRAM_SEND = 2; STATE_DATAGRAM_SEND_REPLY_ACK = 3; STATE_DATAGRAM_WAITFOR = 4; STATE_DATAGRAM_DONE = 5; const STATE_CONFIG_MEM_STREAM_START = 0; STATE_CONFIG_MEM_STREAM_INIT = 1; STATE_CONFIG_MEM_STREAM_WAIT_FOR_INIT_REPLY = 2; STATE_CONFIG_MEM_STREAM_SEND = 3; STATE_CONFIG_MEM_STREAM_WAIT_FOR_PROCEED = 4; STATE_CONFIG_MEM_STREAM_SEND_COMPLETE = 5; STATE_CONFIG_MEM_STREAM_COMPLETE = 6; type TNMRAnetCanBuffer = record MTI: DWord; Payload: array[0..7] of byte; PayloadCount: Byte; end; PNMRAnetCanBuffer = ^TNMRAnetCanBuffer; const DEFAULT_SPEED_STEPS = 28; 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 ); NMRA_LONGADDRESS_MASK_BYTE = $C0; NMRA_LONGADDRESS_MASK_WORD = $C000; type TTrainConfig = record RoadName: TStnipBuffer; TrainClass: TStnipBuffer; RoadNumber: TStnipBuffer; Name: TStnipBuffer; Manufacturer: TStnipBuffer; Owner: TStnipBuffer; TrainID: Word; SpeedStep: Byte; ShortLong: Byte; end; const TRAIN_PROXY_ACTION_NONE = 0; TRAIN_PROXY_ACTION_SPEEDDIR = 1; TRAIN_PROXY_ACTION_FUNCTION = 2; TRAIN_PROXY_ACTION_ESTOP = 3; procedure OPStackDefines_Initialize; var s1: string[128]; NodePool: TNodePool; NULL_NODE_INFO: TNodeInfo; implementation procedure OPStackDefines_Initialize; begin NULL_NODE_INFO.AliasID := 0; NULL_NODE_INFO.ID := NULL_NODE_ID; end; end.