From 83ed23280a0bf2c3fad780e0788c9c6d484341a8 Mon Sep 17 00:00:00 2001 From: PS Date: Sat, 3 Oct 2020 08:46:14 -0700 Subject: [PATCH] Cleaned up network interface, separated SACN out from the actual sending of data, implemented sending over COM ports (remains to be tested), and added information specifying data output modes for com ports to the assembly file format --- src/app/engine/assembly_parser.cpp | 84 ++++++- src/app/engine/foldhaus_addressed_data.h | 64 +++-- src/app/engine/foldhaus_assembly.cpp | 72 +++++- src/app/engine/foldhaus_assembly.h | 28 ++- src/app/engine/foldhaus_network_ordering.h | 48 ++-- .../engine/sacn/{sacn.h => foldhaus_sacn.h} | 13 +- src/app/engine/uart/foldhaus_uart.h | 155 ++++++++++++ src/app/foldhaus_app.cpp | 41 +--- src/app/foldhaus_app.h | 13 +- src/app/foldhaus_platform.h | 38 +-- src/app/platform_win32/win32_foldhaus.cpp | 229 +++++------------- .../platform_win32/win32_foldhaus_serial.h | 84 ++++++- .../platform_win32/win32_foldhaus_socket.h | 140 +++++++++++ src/gs_libs/gs_types.cpp | 3 + 14 files changed, 709 insertions(+), 303 deletions(-) rename src/app/engine/sacn/{sacn.h => foldhaus_sacn.h} (96%) create mode 100644 src/app/engine/uart/foldhaus_uart.h create mode 100644 src/app/platform_win32/win32_foldhaus_socket.h diff --git a/src/app/engine/assembly_parser.cpp b/src/app/engine/assembly_parser.cpp index d23b596..636c11c 100644 --- a/src/app/engine/assembly_parser.cpp +++ b/src/app/engine/assembly_parser.cpp @@ -5,17 +5,33 @@ // #ifndef ASSEMBLY_PARSER_CPP +// TODO(pjs): This is good for meta generation +// ie. It would be great to have +// // enum ident enum prefix +// BEGIN_GEN_ENUM(assembly_field, AssemblyField_) +// // value name gen string of the value name the paired string identifier +// ADD_ENUM_VALUE(AssemblyName, DO_GEN_STRING, "assembly_name") +// ADD_ENUM_VALUE(AssemblyScale, DO_GEN_STRING, "assembly_scale") +// END_GEN_ENUM(assembly_field) + enum assembly_field { AssemblyField_AssemblyName, AssemblyField_AssemblyScale, AssemblyField_AssemblyCenter, AssemblyField_LedStripCount, + AssemblyField_OutputMode, AssemblyField_LedStrip, - AssemblyField_ControlBoxId, - AssemblyField_StartUniverse, - AssemblyField_StartChannel, + + AssemblyField_OutputSACN, + AssemblyField_SACN_StartUniverse, + AssemblyField_SACN_StartChannel, + + AssemblyField_OutputUART, + AssemblyField_UART_Channel, + AssemblyField_UART_ComPort, + AssemblyField_PointPlacementType, AssemblyField_InterpolatePoints, AssemblyField_Start, @@ -34,12 +50,17 @@ global char* AssemblyFieldIdentifiers[] = { "assembly_scale", // AssemblyField_AssemblyScale "assembly_center", // AssemblyField_AssemblyCenter "led_strip_count", // AssemblyField_LedStripCount + "output_mode", // AssemblyField_OutputMode "led_strip", // AssemblyField_LedStrip - "control_box_id", // AssemblyField_ControlBoxId - "start_universe", // AssemblyField_StartUniverse - "start_channel", // AssemblyField_StartChannel + "output_sacn", // AssemblyField_OutputSACN + "start_universe", // AssemblyField_SACN_StartUniverse + "start_channel", // AssemblyField_SACN_StartChannel + + "output_uart", // AssemblyField_OutputUART + "channel", // AssemblyField_UART_Channel + "com_port", // AssemblyField_UART_ComPort "point_placement_type", // AssemblyField_PointPlacementType "interpolate_points", // AssemblyField_InterpolatePoints @@ -158,8 +179,11 @@ TokenizerPushError(assembly_tokenizer* T, char* ErrorString) EatToNewLine(T); } +#define PARSER_FIELD_REQUIRED true +#define PARSER_FIELD_OPTIONAL false + internal bool -ReadFieldIdentifier(assembly_field Field, assembly_tokenizer* T) +ReadFieldIdentifier(assembly_field Field, assembly_tokenizer* T, bool Required = true) { bool Result = false; if (AdvanceIfTokenEquals(T, AssemblyFieldIdentifiers[Field])) @@ -170,10 +194,12 @@ ReadFieldIdentifier(assembly_field Field, assembly_tokenizer* T) } else { + // We always throw an error if we get this far because we know you were trying to + // open the identifier TokenizerPushError(T, "Field identifier is missing a colon"); } } - else + else if (Required) { TokenizerPushError(T, "Field Identifier Invalid"); } @@ -350,10 +376,10 @@ ReadV3Field(assembly_field Field, assembly_tokenizer* T) } internal bool -ReadStructOpening(assembly_field Field, assembly_tokenizer* T) +ReadStructOpening(assembly_field Field, assembly_tokenizer* T, bool Required = true) { bool Result = false; - if (ReadFieldIdentifier(Field, T)) + if (ReadFieldIdentifier(Field, T, Required)) { if (AdvanceIfTokenEquals(T, "{")) { @@ -400,14 +426,46 @@ ParseAssemblyFile(assembly* Assembly, gs_const_string FileName, gs_string FileTe Assembly->StripCount = ReadIntField(AssemblyField_LedStripCount, &Tokenizer); Assembly->Strips = PushArray(&Assembly->Arena, v2_strip, Assembly->StripCount); + gs_string OutputModeString = ReadStringField(AssemblyField_OutputMode, &Tokenizer, Transient); + if (StringsEqual(OutputModeString.ConstString, ConstString("UART"))) + { + Assembly->OutputMode = NetworkProtocol_UART; + } + else if (StringsEqual(OutputModeString.ConstString, ConstString("SACN"))) + { + Assembly->OutputMode = NetworkProtocol_SACN; + } + else + { + TokenizerPushError(&Tokenizer, "Invalid output mode specified."); + } + for (u32 i = 0; i < Assembly->StripCount; i++) { v2_strip* StripAt = Assembly->Strips + i; if (ReadStructOpening(AssemblyField_LedStrip, &Tokenizer)) { - StripAt->ControlBoxID = ReadIntField(AssemblyField_ControlBoxId, &Tokenizer); - StripAt->StartUniverse = ReadIntField(AssemblyField_StartUniverse, &Tokenizer); - StripAt->StartChannel = ReadIntField(AssemblyField_StartChannel, &Tokenizer); + if (ReadStructOpening(AssemblyField_OutputSACN, &Tokenizer, PARSER_FIELD_OPTIONAL)) + { + StripAt->SACNAddr.StartUniverse = ReadIntField(AssemblyField_SACN_StartUniverse, &Tokenizer); + StripAt->SACNAddr.StartChannel = ReadIntField(AssemblyField_SACN_StartChannel, &Tokenizer); + + if (!ReadStructClosing(&Tokenizer)) + { + TokenizerPushError(&Tokenizer, "Struct doesn't close where expected"); + } + } + + if (ReadStructOpening(AssemblyField_OutputUART, &Tokenizer, PARSER_FIELD_OPTIONAL)) + { + StripAt->UARTAddr.Channel = (u8)ReadIntField(AssemblyField_UART_Channel, &Tokenizer); + StripAt->UARTAddr.ComPort = ReadStringField(AssemblyField_UART_ComPort, &Tokenizer, &Assembly->Arena).ConstString; + + if (!ReadStructClosing(&Tokenizer)) + { + TokenizerPushError(&Tokenizer, "Struct doesn't close where expected"); + } + } // TODO(Peter): Need to store this gs_string PointPlacementType = ReadStringField(AssemblyField_PointPlacementType, &Tokenizer, &Assembly->Arena); diff --git a/src/app/engine/foldhaus_addressed_data.h b/src/app/engine/foldhaus_addressed_data.h index 6436a0d..79a84de 100644 --- a/src/app/engine/foldhaus_addressed_data.h +++ b/src/app/engine/foldhaus_addressed_data.h @@ -12,66 +12,86 @@ enum data_buffer_address_type { AddressType_NetworkIP, + AddressType_ComPort, AddressType_Invalid, }; struct addressed_data_buffer { - u8* Memory; - u32 MemorySize; + union + { + struct + { + u8* Memory; + u32 MemorySize; + }; + gs_data Data; + }; data_buffer_address_type AddressType; // IP Address + platform_socket_handle SendSocket; u32 V4SendAddress; u32 SendPort; + // COM + gs_const_string ComPort; + addressed_data_buffer* Next; }; struct addressed_data_buffer_list { + gs_memory_arena* Arena; addressed_data_buffer* Root; addressed_data_buffer* Head; }; -internal addressed_data_buffer* -AddressedDataBufferList_Push(addressed_data_buffer_list* List, u32 BufferSize, gs_memory_arena* Storage) +internal void +AddressedDataBufferList_Clear(addressed_data_buffer_list* List) { - addressed_data_buffer* Result = PushStruct(Storage, addressed_data_buffer); + List->Root = 0; + List->Head = 0; + ClearArena(List->Arena); +} + +internal addressed_data_buffer* +AddressedDataBufferList_PushEmpty(addressed_data_buffer_list* List) +{ + addressed_data_buffer* Result = PushStruct(List->Arena, addressed_data_buffer); *Result = {0}; - Result->MemorySize = BufferSize; - Result->Memory = PushArray(Storage, u8, Result->MemorySize); + Result->MemorySize = 0; + Result->Memory = 0; SLLPushOrInit(List->Root, List->Head, Result); return Result; } +internal addressed_data_buffer* +AddressedDataBufferList_Push(addressed_data_buffer_list* List, u32 BufferSize) +{ + addressed_data_buffer* Result = AddressedDataBufferList_PushEmpty(List); + Result->MemorySize = BufferSize; + Result->Memory = PushArray(List->Arena, u8, Result->MemorySize); + return Result; +} + internal void -AddressedDataBuffer_SetNetworkAddress(addressed_data_buffer* Buffer, u32 V4SendAddress, u32 SendPort) +AddressedDataBuffer_SetNetworkAddress(addressed_data_buffer* Buffer, platform_socket_handle SendSocket, u32 V4SendAddress, u32 SendPort) { Buffer->AddressType = AddressType_NetworkIP; + Buffer->SendSocket = SendSocket; Buffer->V4SendAddress = V4SendAddress; Buffer->SendPort = SendPort; } internal void -AddressedDataBuffer_Send(addressed_data_buffer Buffer, platform_socket_handle SendSocket, context Context) +AddressedDataBuffer_SetCOMPort(addressed_data_buffer* Buffer, gs_const_string ComPort) { - u32 V4SendAddress = Buffer.V4SendAddress; - Context.PlatformSendTo(SendSocket, Buffer.V4SendAddress, Buffer.SendPort, (const char*)Buffer.Memory, Buffer.MemorySize, 0); -} - -internal void -AddressedDataBufferList_SendAll(addressed_data_buffer_list OutputData, platform_socket_handle SendSocket, context Context) -{ - for (addressed_data_buffer* BufferAt = OutputData.Root; - BufferAt != 0; - BufferAt = BufferAt->Next) - { - AddressedDataBuffer_Send(*BufferAt, SendSocket, Context); - } + Buffer->AddressType = AddressType_ComPort; + Buffer->ComPort = ComPort; } #define FOLDHAUS_ADDRESSED_DATA_H diff --git a/src/app/engine/foldhaus_assembly.cpp b/src/app/engine/foldhaus_assembly.cpp index afb3919..5be7342 100644 --- a/src/app/engine/foldhaus_assembly.cpp +++ b/src/app/engine/foldhaus_assembly.cpp @@ -5,6 +5,72 @@ // #ifndef FOLDHAUS_ASSEMBLY_CPP +/////////////////////////// +// +// Assembly Array +// +/////////////////////////// + +internal assembly_array +AssemblyArray_Create(u32 CountMax, gs_memory_arena* Storage) +{ + assembly_array Result = {0}; + Result.CountMax = CountMax; + Result.Values = PushArray(Storage, assembly, Result.CountMax); + return Result; +} + +internal u32 +AssemblyArray_Push(assembly_array* Array, assembly Assembly) +{ + Assert(Array->Count < Array->CountMax); + u32 Index = Array->Count++; + Array->Values[Index] = Assembly; + return Index; +} + +internal assembly* +AssemblyArray_Take(assembly_array* Array) +{ + u32 Index = AssemblyArray_Push(Array, {}); + assembly* Result = Array->Values + Index; + return Result; +} + +internal void +AssemblyArray_RemoveAt(assembly_array* Array, u32 Index) +{ + u32 LastAssemblyIndex = --Array->Count; + Array->Values[Index] = Array->Values[LastAssemblyIndex]; +} + +typedef bool assembly_array_filter_proc(assembly A); +bool AssemblyFilter_OutputsViaSACN(assembly A) { return A.OutputMode == NetworkProtocol_SACN; } +bool AssemblyFilter_OutputsViaUART(assembly A) { return A.OutputMode == NetworkProtocol_UART; } + +internal assembly_array +AssemblyArray_Filter(assembly_array Array, assembly_array_filter_proc* Filter, gs_memory_arena* Storage) +{ + assembly_array Result = AssemblyArray_Create(Array.Count, Storage); + + for (u32 i = 0; i < Array.Count; i++) + { + assembly At = Array.Values[i]; + if (Filter(At)) + { + AssemblyArray_Push(&Result, At); + } + } + + return Result; +} + +/////////////////////////// +// +// LedSystem +// +/////////////////////////// + internal led_system LedSystemInitialize(gs_allocator PlatformMemory, u32 BuffersMax) { @@ -116,8 +182,7 @@ LoadAssembly (assembly_array* Assemblies, led_system* LedSystem, gs_memory_arena s32 IndexOfLastSlash = FindLast(Path, '\\'); gs_const_string FileName = Substring(Path, IndexOfLastSlash + 1, Path.Length); - Assert(Assemblies->Count < Assemblies->CountMax); - assembly* NewAssembly = &Assemblies->Values[Assemblies->Count++]; + assembly* NewAssembly = AssemblyArray_Take(Assemblies); NewAssembly->Arena = CreateMemoryArena(Context.ThreadContext.Allocator); if (ParseAssemblyFile(NewAssembly, FileName, AssemblyFileText, Scratch)) @@ -143,8 +208,7 @@ UnloadAssembly (u32 AssemblyIndex, app_state* State, context Context) assembly* Assembly = &State->Assemblies.Values[AssemblyIndex]; LedSystemFreeBuffer(&State->LedSystem, Assembly->LedBufferIndex); FreeMemoryArena(&Assembly->Arena); - u32 LastAssemblyIndex = --State->Assemblies.Count; - State->Assemblies.Values[AssemblyIndex] = State->Assemblies.Values[LastAssemblyIndex]; + AssemblyArray_RemoveAt(&State->Assemblies, AssemblyIndex); } // Querying Assemblies diff --git a/src/app/engine/foldhaus_assembly.h b/src/app/engine/foldhaus_assembly.h index a4ebd0e..c702a3b 100644 --- a/src/app/engine/foldhaus_assembly.h +++ b/src/app/engine/foldhaus_assembly.h @@ -5,6 +5,15 @@ // #ifndef FOLDHAUS_ASSEMBLY_H +enum network_protocol +{ + NetworkProtocol_SACN, + NetworkProtocol_ArtNet, + NetworkProtocol_UART, + + NetworkProtocol_Count, +}; + union pixel { struct @@ -40,13 +49,24 @@ struct v2_tag u64 ValueHash; }; +struct strip_sacn_addr +{ + s32 StartUniverse; + s32 StartChannel; +}; + +struct strip_uart_addr +{ + u8 Channel; + gs_const_string ComPort; +}; + struct v2_strip { s32 ControlBoxID; // TODO(Peter): I don't think we need this anymore - // TODO(Peter): Add in info for Serial, ArtNet, etc. - s32 StartUniverse; - s32 StartChannel; + strip_sacn_addr SACNAddr; + strip_uart_addr UARTAddr; // TODO(Peter): When we create more ways to calculate points, this needs to become // a type enum and a union @@ -81,6 +101,8 @@ struct assembly u32 StripCount; v2_strip* Strips; + + network_protocol OutputMode; }; struct assembly_array diff --git a/src/app/engine/foldhaus_network_ordering.h b/src/app/engine/foldhaus_network_ordering.h index 65b60c7..05e5afd 100644 --- a/src/app/engine/foldhaus_network_ordering.h +++ b/src/app/engine/foldhaus_network_ordering.h @@ -6,7 +6,7 @@ #ifndef FOLDHAUS_NETWORK_ORDERING_H // Packs a u8 to a known big endian buffer -inline u8* +inline u8* PackB1(u8* ptr, u8 val) { *ptr = val; @@ -14,14 +14,14 @@ PackB1(u8* ptr, u8 val) } //Unpacks a u8 from a known big endian buffer -inline u8 +inline u8 UpackB1(const u8* ptr) { return *ptr; } //Packs a u8 to a known little endian buffer -inline u8* +inline u8* PackL1(u8* ptr, u8 val) { *ptr = val; @@ -29,30 +29,40 @@ PackL1(u8* ptr, u8 val) } //Unpacks a u8 from a known little endian buffer -inline u8 +inline u8 UpackL1(const u8* ptr) { return *ptr; } //Packs a u16 to a known big endian buffer -inline u8* +inline u16 +PackB2(u16 Value) +{ + u16 Result = 0; + u8* Array = (u8*)&Result; + Array[1] = (u8)(Value & 0xFF); + Array[0] = (u8)((Value & 0xFF00) >> 8); + return Result; +} + +inline u8* PackB2(u8* ptr, u16 val) { - ptr[1] = (u8)(val & 0xff); + ptr[1] = (u8)(val & 0xff); ptr[0] = (u8)((val & 0xff00) >> 8); return ptr + sizeof(val); } //Unpacks a u16 from a known big endian buffer -inline u16 +inline u16 UpackB2(const u8* ptr) { return (u16)(ptr[1] | ptr[0] << 8); } //Packs a u16 to a known little endian buffer -inline u8* +inline u8* PackL2(u8* ptr, u16 val) { *((u16*)ptr) = val; @@ -60,14 +70,14 @@ PackL2(u8* ptr, u16 val) } //Unpacks a u16 from a known little endian buffer -inline u16 +inline u16 UpackL2(const u8* ptr) { return *((u16*)ptr); } //Packs a u32 to a known big endian buffer -inline u8* +inline u8* PackB4(u8* ptr, u32 val) { ptr[3] = (u8) (val & 0xff); @@ -78,14 +88,14 @@ PackB4(u8* ptr, u32 val) } //Unpacks a u32 from a known big endian buffer -inline u32 +inline u32 UpackB4(const u8* ptr) { return (u32)(ptr[3] | (ptr[2] << 8) | (ptr[1] << 16) | (ptr[0] << 24)); } //Packs a u32 to a known little endian buffer -inline u8* +inline u8* PackL4(u8* ptr, u32 val) { *((u32*)ptr) = val; @@ -93,14 +103,14 @@ PackL4(u8* ptr, u32 val) } //Unpacks a u32 from a known little endian buffer -inline u32 +inline u32 UpackL4(const u8* ptr) { return *((u32*)ptr); } //Packs a u64 to a known big endian buffer -inline u8* +inline u8* PackB8(u8* ptr, u64 val) { ptr[7] = (u8) (val & 0xff); @@ -115,17 +125,17 @@ PackB8(u8* ptr, u64 val) } //Unpacks a uint64 from a known big endian buffer -inline u64 +inline u64 UpackB8(const u8* ptr) { return ((u64)ptr[7]) | (((u64)ptr[6]) << 8) | (((u64)ptr[5]) << 16) | - (((u64)ptr[4]) << 24) | (((u64)ptr[3]) << 32) | - (((u64)ptr[2]) << 40) | (((u64)ptr[1]) << 48) | + (((u64)ptr[4]) << 24) | (((u64)ptr[3]) << 32) | + (((u64)ptr[2]) << 40) | (((u64)ptr[1]) << 48) | (((u64)ptr[0]) << 56); } //Packs a u64 to a known little endian buffer -inline u8* +inline u8* PackL8(u8* ptr, u64 val) { *((u64*)ptr) = val; @@ -133,7 +143,7 @@ PackL8(u8* ptr, u64 val) } //Unpacks a u64 from a known little endian buffer -inline u64 +inline u64 UpackL8(const u8* ptr) { return *((u64*)ptr); diff --git a/src/app/engine/sacn/sacn.h b/src/app/engine/sacn/foldhaus_sacn.h similarity index 96% rename from src/app/engine/sacn/sacn.h rename to src/app/engine/sacn/foldhaus_sacn.h index a12c4b8..6004361 100644 --- a/src/app/engine/sacn/sacn.h +++ b/src/app/engine/sacn/foldhaus_sacn.h @@ -208,6 +208,7 @@ InitStreamHeader (u8* Buffer, s32 BufferSize, cid CID ) { + // TODO(pjs): Replace packing with gs_memory_cursor u8* Cursor = Buffer; @@ -285,6 +286,7 @@ InitStreamHeader (u8* Buffer, s32 BufferSize, Cursor = PackB1(Cursor, StartCode); Assert(Cursor - Buffer == STREAM_HEADER_SIZE); + } // @@ -306,7 +308,6 @@ SACN_Initialize (context Context) internal void SACN_Cleanup(streaming_acn* SACN, context Context) { - Context.PlatformCloseSocket(SACN->SendSocket); } internal void @@ -360,7 +361,7 @@ SACN_FillBufferWithLeds(u8* BufferStart, u32 BufferSize, v2_strip Strip, led_buf } internal void -SACN_BuildOutputData(streaming_acn* SACN, addressed_data_buffer_list* Output, assembly_array Assemblies, led_system* LedSystem, gs_memory_arena* OutputStorage) +SACN_BuildOutputData(streaming_acn* SACN, addressed_data_buffer_list* Output, assembly_array Assemblies, led_system* LedSystem) { SACN_UpdateSequence(SACN); @@ -378,13 +379,13 @@ SACN_BuildOutputData(streaming_acn* SACN, addressed_data_buffer_list* Output, as { v2_strip StripAt = Assembly.Strips[StripIdx]; - u32 V4SendAddress = SACN_GetUniverseSendAddress(StripAt.StartUniverse); + u32 V4SendAddress = SACN_GetUniverseSendAddress(StripAt.SACNAddr.StartUniverse); u32 SendPort = DEFAULT_STREAMING_ACN_PORT; - addressed_data_buffer* Data = AddressedDataBufferList_Push(Output, BufferSize, OutputStorage); - AddressedDataBuffer_SetNetworkAddress(Data, V4SendAddress, SendPort); + addressed_data_buffer* Data = AddressedDataBufferList_Push(Output, BufferSize); + AddressedDataBuffer_SetNetworkAddress(Data, SACN->SendSocket, V4SendAddress, SendPort); - SACN_PrepareBufferHeader(StripAt.StartUniverse, Data->Memory, Data->MemorySize, BufferHeaderSize, *SACN); + SACN_PrepareBufferHeader(StripAt.SACNAddr.StartUniverse, Data->Memory, Data->MemorySize, BufferHeaderSize, *SACN); SACN_FillBufferWithLeds(Data->Memory + BufferHeaderSize, BufferBodySize, StripAt, *LedBuffer); } } diff --git a/src/app/engine/uart/foldhaus_uart.h b/src/app/engine/uart/foldhaus_uart.h new file mode 100644 index 0000000..a80b7ff --- /dev/null +++ b/src/app/engine/uart/foldhaus_uart.h @@ -0,0 +1,155 @@ +// +// File: foldhaus_uart.h +// Author: Peter Slattery +// Creation Date: 2020-10-01 +// +#ifndef FOLDHAUS_UART_H + +enum uart_record_type +{ + UART_SET_CHANNEL_WS2812 = 1, + UART_DRAW_ALL = 2, +}; + +// NOTE(pjs): these are data packet structures and need to have 1 byte packing +#pragma pack(push, 1) + +struct uart_header +{ + u8 MagicNumber[4]; + u8 Channel; + u8 RecordType; +}; + +struct uart_channel +{ + u8 ElementsCount; + + u8 RedIndex; + u8 GreenIndex; + u8 BlueIndex; + u8 WhiteIndex; + + u16 PixelsCount; +}; + +struct uart_footer +{ + u32 CRC; +}; + +#pragma pack(pop) + +global u32 UART_CRCTable[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +internal void +UART_FillHeader(uart_header* Header, u8 Channel, u8 RecordType) +{ + Header->MagicNumber[0] = 'U'; + Header->MagicNumber[1] = 'P'; + Header->MagicNumber[2] = 'X'; + Header->MagicNumber[3] = 'L'; + + Header->Channel = Channel; + Header->RecordType = RecordType; +} + +internal void +UART_FillFooter(uart_footer* Footer, u8* BufferStart) +{ + // Calculate the CRC + u32 CRC = 0xFFFFFFFF; + u32 BytesCount = (u8*)Footer - BufferStart; + for (u32 i = 0; i < BytesCount; i++) + { + u8 At = BufferStart[i]; + + // Cameron's Version + CRC = UART_CRCTable[(CRC ^ At) & 0x0F] ^ (CRC >> 4); + CRC = UART_CRCTable[(CRC ^ (At >> 4)) & 0x0F] ^ (CRC >> 4); + +#if 0 + // The Libraries Version + CRC = (UART_CRCTable[(CRC ^ At) & 0xFF] ^ (CRC >> 8)) & 0xFFFFFFFF; +#endif + } + + Footer->CRC = CRC; +} + +internal void +UART_SetChannelBuffer_Create(addressed_data_buffer_list* Output, uart_channel ChannelSettings, v2_strip Strip, led_buffer LedBuffer) +{ + // NOTE(pjs): This is just here because the information is duplicated and I want to be sure + // to catch the error where they are different + Assert(ChannelSettings.PixelsCount == Strip.LedCount); + + u32 BufferSize = sizeof(uart_header) + sizeof(uart_channel); + BufferSize += ChannelSettings.ElementsCount * ChannelSettings.PixelsCount; + BufferSize += sizeof(uart_footer); + + addressed_data_buffer* Buffer = AddressedDataBufferList_Push(Output, BufferSize); + AddressedDataBuffer_SetCOMPort(Buffer, Strip.UARTAddr.ComPort); + + gs_memory_cursor WriteCursor = CreateMemoryCursor(Buffer->Data); + + uart_header* Header = PushStructOnCursor(&WriteCursor, uart_header); + UART_FillHeader(Header, Strip.UARTAddr.Channel, UART_SET_CHANNEL_WS2812); + + uart_channel* Channel = PushStructOnCursor(&WriteCursor, uart_channel); + *Channel = ChannelSettings; + + for (u32 i = 0; i < Channel->PixelsCount; i++) + { + u32 LedIndex = Strip.LedLUT[i]; + pixel Color = LedBuffer.Colors[LedIndex]; + + u8* OutputPixel = PushArrayOnCursor(&WriteCursor, u8, 3); + + OutputPixel[Channel->RedIndex] = Color.R; + OutputPixel[Channel->GreenIndex] = Color.G; + OutputPixel[Channel->BlueIndex] = Color.B; + + if (OutputPixel[Channel->ElementsCount == 4]) + { + // TODO(pjs): Calculate white from the RGB components? + // Generally we just need a good way to handle the white channel, + // both in the renderer and in output + + //OutputPixel[Channel->WhiteIndex] = Color.W; + } + } + + uart_footer* Footer = PushStructOnCursor(&WriteCursor, uart_footer); + UART_FillFooter(Footer, Buffer->Memory); +} + +internal void +UART_BuildOutputData(addressed_data_buffer_list* Output, assembly_array Assemblies, led_system* LedSystem) +{ + uart_channel ChannelSettings = {0}; + ChannelSettings.ElementsCount = 3; + ChannelSettings.RedIndex = 1; + ChannelSettings.GreenIndex = 2; + ChannelSettings.BlueIndex = 3; + ChannelSettings.WhiteIndex = 0; + + for (u32 AssemblyIdx = 0; AssemblyIdx < Assemblies.Count; AssemblyIdx++) + { + assembly Assembly = Assemblies.Values[AssemblyIdx]; + led_buffer* LedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex); + + for (u32 StripIdx = 0; StripIdx < Assembly.StripCount; StripIdx++) + { + v2_strip StripAt = Assembly.Strips[StripIdx]; + ChannelSettings.PixelsCount = StripAt.LedCount; + UART_SetChannelBuffer_Create(Output, ChannelSettings, StripAt, *LedBuffer); + } + } +} + +#define FOLDHAUS_UART_H +#endif // FOLDHAUS_UART_H \ No newline at end of file diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index 8914b3c..ed12a0a 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -25,8 +25,7 @@ INITIALIZE_APPLICATION(InitializeApplication) State->Permanent = CreateMemoryArena(Context.ThreadContext.Allocator); State->Transient = Context.ThreadContext.Transient; - State->Assemblies.CountMax = 8; - State->Assemblies.Values = PushArray(&State->Permanent, assembly, State->Assemblies.CountMax); + State->Assemblies = AssemblyArray_Create(8, &State->Permanent); State->GlobalLog = PushStruct(State->Transient, event_log); *State->GlobalLog = {0}; @@ -126,9 +125,6 @@ INITIALIZE_APPLICATION(InitializeApplication) #if 1 gs_const_string SculpturePath = ConstString("data/blumen_lumen_v2.fold"); LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath, State->GlobalLog); - - SculpturePath = ConstString("data/radialumia_v2.fold"); - LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath, State->GlobalLog); #endif State->PixelsToWorldScale = .01f; @@ -343,38 +339,13 @@ UPDATE_AND_RENDER(UpdateAndRender) { // NOTE(pjs): Building data buffers to be sent out to the sculpture - - addressed_data_buffer_list OutputData = {0}; - switch (State->NetworkProtocol) - { - case NetworkProtocol_SACN: - { - SACN_BuildOutputData(&State->SACN, &OutputData, State->Assemblies, &State->LedSystem, State->Transient); - }break; - - case NetworkProtocol_UART: - { - //UART_BuildOutputData(&OutputData, State, State->Transient); - }break; - - case NetworkProtocol_ArtNet: - InvalidDefaultCase; - } - - // NOTE(pjs): Executing the job to actually send the data - if (0) - { - // TODO(pjs): This should happen on another thread - AddressedDataBufferList_SendAll(OutputData, State->SACN.SendSocket, *Context); - - /* - Saved this lien as an example of pushing onto a queue - Context->GeneralWorkQueue->PushWorkOnQueue(Context->GeneralWorkQueue, SACNSendDMXBufferListJob, Job, "SACN Send Data Job"); - */ - } + // This array is used on the platform side to actually send the information + assembly_array SACNAssemblies = AssemblyArray_Filter(State->Assemblies, AssemblyFilter_OutputsViaSACN, State->Transient); + assembly_array UARTAssemblies = AssemblyArray_Filter(State->Assemblies, AssemblyFilter_OutputsViaUART, State->Transient); + SACN_BuildOutputData(&State->SACN, OutputData, SACNAssemblies, &State->LedSystem); + UART_BuildOutputData(OutputData, UARTAssemblies, &State->LedSystem); } - PushRenderOrthographic(RenderBuffer, State->WindowBounds); PushRenderClearScreen(RenderBuffer); diff --git a/src/app/foldhaus_app.h b/src/app/foldhaus_app.h index e3b5532..51938b4 100644 --- a/src/app/foldhaus_app.h +++ b/src/app/foldhaus_app.h @@ -18,8 +18,8 @@ #include "engine/foldhaus_assembly.h" #include "engine/assembly_parser.cpp" -#include "engine/foldhaus_addressed_data.h" -#include "engine/sacn/sacn.h" +#include "engine/sacn/foldhaus_sacn.h" +#include "engine/uart/foldhaus_uart.h" typedef struct app_state app_state; @@ -33,15 +33,6 @@ typedef struct app_state app_state; #include "engine/animation/foldhaus_animation.h" -enum network_protocol -{ - NetworkProtocol_SACN, - NetworkProtocol_ArtNet, - NetworkProtocol_UART, - - NetworkProtocol_Count, -}; - struct app_state { gs_memory_arena Permanent; diff --git a/src/app/foldhaus_platform.h b/src/app/foldhaus_platform.h index 4f066d4..96a7ec7 100644 --- a/src/app/foldhaus_platform.h +++ b/src/app/foldhaus_platform.h @@ -33,7 +33,18 @@ global debug_services* GlobalDebugServices; //#include "..\gs_libs\gs_vector_matrix.h" #include "..\gs_libs\gs_input.h" +struct platform_network_address +{ + s32 Family; + u16 Port; + u32 Address; +}; + +typedef s32 platform_socket_handle; +typedef s32 platform_network_address_handle; + #include "foldhaus_renderer.h" +#include "engine/foldhaus_addressed_data.h" typedef struct context context; @@ -42,7 +53,7 @@ typedef struct context context; #define INITIALIZE_APPLICATION(name) void name(context Context) typedef INITIALIZE_APPLICATION(initialize_application); -#define UPDATE_AND_RENDER(name) void name(context* Context, input_queue InputQueue, render_command_buffer* RenderBuffer) +#define UPDATE_AND_RENDER(name) void name(context* Context, input_queue InputQueue, render_command_buffer* RenderBuffer, addressed_data_buffer_list* OutputData) typedef UPDATE_AND_RENDER(update_and_render); #define RELOAD_STATIC_DATA(name) void name(context Context, debug_services* DebugServices) @@ -102,31 +113,9 @@ struct texture_buffer #define PLATFORM_GET_GPU_TEXTURE_HANDLE(name) s32 name(u8* Memory, s32 Width, s32 Height) typedef PLATFORM_GET_GPU_TEXTURE_HANDLE(platform_get_gpu_texture_handle); -struct platform_network_address -{ - s32 Family; - u16 Port; - u32 Address; -}; - -typedef s32 platform_socket_handle; -typedef s32 platform_network_address_handle; - #define PLATFORM_GET_SOCKET_HANDLE(name) platform_socket_handle name(s32 Multicast_TimeToLive) typedef PLATFORM_GET_SOCKET_HANDLE(platform_get_socket_handle); -#define PLATFORM_GET_SEND_ADDRESS_HANDLE(name) platform_network_address_handle name(s32 AddressFamily, u16 Port, u32 Address) -typedef PLATFORM_GET_SEND_ADDRESS_HANDLE(platform_get_send_address); - -#define PLATFORM_SET_SOCKET_OPTION(name) s32 name(platform_socket_handle SocketHandle, s32 Level, s32 Option, const char* OptionValue, s32 OptionLength) -typedef PLATFORM_SET_SOCKET_OPTION(platform_set_socket_option); - -#define PLATFORM_SEND_TO(name) s32 name(platform_socket_handle SocketHandle, u32 Address, u32 Port, const char* Buffer, s32 BufferLength, s32 Flags) -typedef PLATFORM_SEND_TO(platform_send_to); - -#define PLATFORM_CLOSE_SOCKET(name) void name(platform_socket_handle SocketHandle) -typedef PLATFORM_CLOSE_SOCKET(platform_close_socket); - // Font struct platform_font_info { @@ -209,9 +198,6 @@ struct context platform_draw_font_codepoint* PlatformDrawFontCodepoint; platform_get_socket_handle* PlatformGetSocketHandle; - platform_set_socket_option* PlatformSetSocketOption; - platform_send_to* PlatformSendTo; - platform_close_socket* PlatformCloseSocket; }; diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index 338f957..dc076eb 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -22,6 +22,7 @@ #include "win32_foldhaus_timing.h" #include "win32_foldhaus_work_queue.h" #include "win32_foldhaus_serial.h" +#include "win32_foldhaus_socket.h" #include "../foldhaus_renderer.cpp" @@ -40,105 +41,6 @@ PLATFORM_GET_GPU_TEXTURE_HANDLE(Win32GetGPUTextureHandle) return Handle; } -struct win32_socket -{ - SOCKET Socket; -}; - -#define SOCKET_DICTIONARY_GROW_SIZE 32 -s32 Win32SocketHandleMax; -s32 Win32SocketHandleCount; -win32_socket* SocketValues; - -PLATFORM_SET_SOCKET_OPTION(Win32SetSocketOption) -{ - s32 SocketIndex = (s32)SocketHandle; - Assert(SocketIndex < Win32SocketHandleCount); - int Error = setsockopt(SocketValues[SocketIndex].Socket, Level, Option, OptionValue, OptionLength); - if (Error == SOCKET_ERROR) - { - Error = WSAGetLastError(); - // TODO(Peter): :ErrorLogging - } - - return Error; -} - -PLATFORM_GET_SOCKET_HANDLE(Win32GetSocketHandle) -{ - // NOTE(Peter): These used to be passed in as paramters, but we only use this function - // with AF_INET, SOCK_DGRAM, and Protocol = 0. These are also platform specific values - // so I was having to include windows.h in the platform agnostic code to accomodate that - // function signature. - s32 AddressFamily = AF_INET; - s32 Type = SOCK_DGRAM; - s32 Protocol = 0; - - if (Win32SocketHandleCount >= Win32SocketHandleMax) - { - s32 NewDictionaryMax = Win32SocketHandleMax + SOCKET_DICTIONARY_GROW_SIZE; - s32 NewDictionaryDataSize = NewDictionaryMax * sizeof(win32_socket); - u8* DictionaryMemory = (u8*)Win32Alloc(NewDictionaryDataSize, 0); - Assert(DictionaryMemory); - - win32_socket* NewValues = (win32_socket*)(DictionaryMemory); - if (SocketValues) - { - CopyMemoryTo(SocketValues, NewValues, sizeof(win32_socket) * NewDictionaryMax); - Win32Free((u8*)SocketValues, sizeof(win32_socket) * Win32SocketHandleCount); - } - SocketValues = NewValues; - - Win32SocketHandleMax = NewDictionaryMax; - } - - Assert(Win32SocketHandleCount < Win32SocketHandleMax); - s32 NewSocketIndex = Win32SocketHandleCount++; - - SocketValues[NewSocketIndex].Socket = socket(AddressFamily, Type, Protocol); - - int Error = Win32SetSocketOption(NewSocketIndex, IPPROTO_IP, IP_MULTICAST_TTL, - (const char*)(&Multicast_TimeToLive), sizeof(Multicast_TimeToLive)); - - return (platform_socket_handle)NewSocketIndex; -} - -PLATFORM_SEND_TO(Win32SendTo) -{ - s32 SocketIndex = (s32)SocketHandle; - Assert(SocketIndex < Win32SocketHandleCount); - - sockaddr_in SockAddress = {}; - SockAddress.sin_family = AF_INET; - SockAddress.sin_port = HostToNetU16(Port); - SockAddress.sin_addr.s_addr = HostToNetU32(Address); - - s32 LengthSent = sendto(SocketValues[SocketIndex].Socket, Buffer, BufferLength, Flags, (sockaddr*)&SockAddress, sizeof(sockaddr_in)); - - if (LengthSent == SOCKET_ERROR) - { - s32 Error = WSAGetLastError(); - if (Error == 10051) - { - } - else - { - // TODO(Peter): :ErrorLogging - InvalidCodePath; - } - } - - return LengthSent; -} - -PLATFORM_CLOSE_SOCKET(Win32CloseSocket) -{ - s32 SocketIndex = (s32)SocketHandle; - Assert(SocketIndex < Win32SocketHandleCount); - - closesocket(SocketValues[SocketIndex].Socket); -} - HDC FontDrawingDC; HBITMAP FontBitmap; HFONT CurrentFont; @@ -462,40 +364,6 @@ Win32LoadSystemCursor(char* CursorIdentifier) return Result; } -internal void -PrintMatrix(m44 M, gs_thread_context Context) -{ - gs_string PrintString = AllocatorAllocString(Context.Allocator, 256); - PrintF(&PrintString, "[\n %f %f %f %f\n %f %f %f %f\n %f %f %f %f\n %f %f %f %f\n]\n", - M.Array[0], M.Array[1], M.Array[2], M.Array[3], - M.Array[4], M.Array[5], M.Array[6], M.Array[7], - M.Array[8], M.Array[9], M.Array[10], M.Array[11], - M.Array[12], M.Array[13], M.Array[14], M.Array[15]); - NullTerminate(&PrintString); - OutputDebugStringA(PrintString.Str); -} - -v4 PerspectiveDivide(v4 A) -{ - v4 Result = {0, 0, 0, 1}; - Result.x = A.x / A.w; - Result.y = A.y / A.w; - Result.z = A.z / A.w; - Result.w = A.w; - return Result; -} -v4 ToScreen(v4 P, rect2 WindowBounds) -{ - v4 Result = P; - Result.x = RemapR32(P.x, -1, 1, WindowBounds.Min.x, WindowBounds.Max.x); - Result.y = RemapR32(P.y, -1, 1, WindowBounds.Min.y, WindowBounds.Max.y); - return Result; -} - -// -// Serial -// - int WINAPI WinMain ( HINSTANCE HInstance, @@ -506,19 +374,6 @@ WinMain ( { gs_thread_context ThreadContext = Win32CreateThreadContext(); - - { - gs_const_string TestString = ConstString("Hello World!\nTesting\n"); - - HANDLE SerialPortHandle = Win32SerialPort_Open("COM5"); - Win32SerialPort_SetState(SerialPortHandle, 9600, 8, 0, 1); - Win32SerialPort_Write(SerialPortHandle, StringToData(TestString)); - Win32SerialPort_Close(SerialPortHandle); - } - - - - MainWindow = Win32CreateWindow (HInstance, "Foldhaus", 1440, 768, HandleWindowEvents); Win32UpdateWindowDimension(&MainWindow); @@ -528,12 +383,23 @@ WinMain ( OpenGLWindowInfo.DepthBits = 0; CreateOpenGLWindowContext(OpenGLWindowInfo, &MainWindow); + s32 InitialMemorySize = MB(64); + u8* InitialMemory = (u8*)Win32Alloc(InitialMemorySize, 0); + context Context = {}; + Context.ThreadContext = ThreadContext; + Context.MemorySize = InitialMemorySize; + Context.MemoryBase = InitialMemory; + Context.WindowBounds = rect2{v2{0, 0}, v2{(r32)MainWindow.Width, (r32)MainWindow.Height}}; + Context.Mouse = {0}; + + gs_memory_arena PlatformPermanent = CreateMemoryArena(Context.ThreadContext.Allocator); + s64 PerformanceCountFrequency = GetPerformanceFrequency(); s64 LastFrameEnd = GetWallClock(); r32 TargetSecondsPerFrame = 1 / 60.0f; r32 LastFrameSecondsElapsed = 0.0f; - GlobalDebugServices = (debug_services*)malloc(sizeof(debug_services)); + GlobalDebugServices = PushStruct(&PlatformPermanent, debug_services); s32 DebugThreadCount = PLATFORM_THREAD_COUNT + 1; InitDebugServices(GlobalDebugServices, PerformanceCountFrequency, @@ -556,7 +422,7 @@ WinMain ( worker_thread_info* WorkerThreads = 0; if (PLATFORM_THREAD_COUNT > 0) { - WorkerThreads = (worker_thread_info*)malloc(sizeof(worker_thread_info) * PLATFORM_THREAD_COUNT); + WorkerThreads = PushArray(&PlatformPermanent, worker_thread_info, PLATFORM_THREAD_COUNT); } HANDLE WorkQueueSemaphoreHandle = CreateSemaphoreEx(0, 0, PLATFORM_THREAD_COUNT, 0, 0, SEMAPHORE_ALL_ACCESS); @@ -564,7 +430,7 @@ WinMain ( gs_work_queue WorkQueue = {}; WorkQueue.SemaphoreHandle = &WorkQueueSemaphoreHandle; WorkQueue.JobsMax = 512; - WorkQueue.Jobs = (gs_threaded_job*)Win32Alloc(sizeof(gs_threaded_job) * WorkQueue.JobsMax, 0); + WorkQueue.Jobs = PushArray(&PlatformPermanent, gs_threaded_job, WorkQueue.JobsMax); WorkQueue.NextJobIndex = 0; WorkQueue.PushWorkOnQueue = Win32PushWorkOnQueue; WorkQueue.CompleteQueueWork = Win32DoQueueWorkUntilDone; @@ -577,15 +443,6 @@ WinMain ( WorkerThreads[i].Handle = CreateThread(0, 0, &WorkerThreadProc, (void*)&WorkerThreads[i], 0, 0); } - s32 InitialMemorySize = MB(64); - u8* InitialMemory = (u8*)Win32Alloc(InitialMemorySize, 0); - context Context = {}; - Context.ThreadContext = ThreadContext; - Context.MemorySize = InitialMemorySize; - Context.MemoryBase = InitialMemory; - Context.WindowBounds = rect2{v2{0, 0}, v2{(r32)MainWindow.Width, (r32)MainWindow.Height}}; - Context.Mouse = {0}; - // Cursors HCURSOR CursorArrow = Win32LoadSystemCursor(IDC_ARROW); HCURSOR CursorPointer = Win32LoadSystemCursor(IDC_HAND); @@ -599,9 +456,6 @@ WinMain ( Context.GeneralWorkQueue = &WorkQueue; Context.PlatformGetGPUTextureHandle = Win32GetGPUTextureHandle; Context.PlatformGetSocketHandle = Win32GetSocketHandle; - Context.PlatformSetSocketOption = Win32SetSocketOption; - Context.PlatformSendTo = Win32SendTo; - Context.PlatformCloseSocket = Win32CloseSocket; Context.PlatformGetFontInfo = Win32GetFontInfo; Context.PlatformDrawFontCodepoint = Win32DrawFontCodepoint; @@ -619,11 +473,18 @@ WinMain ( WSADATA WSAData; WSAStartup(MAKEWORD(2, 2), &WSAData); + Win32Sockets = Win32SocketArray_Create(16, &PlatformPermanent); + + Win32SerialArray_Create(ThreadContext); s32 RenderMemorySize = MB(12); - u8* RenderMemory = (u8*)Win32Alloc(RenderMemorySize, 0); + u8* RenderMemory = PushSize(&PlatformPermanent, RenderMemorySize); render_command_buffer RenderBuffer = AllocateRenderCommandBuffer(RenderMemory, RenderMemorySize, Win32Realloc); + addressed_data_buffer_list OutputData = {}; + OutputData.Arena = AllocatorAllocStruct(Context.ThreadContext.Allocator, gs_memory_arena); + *OutputData.Arena = CreateMemoryArena(Context.ThreadContext.Allocator); + Context.InitializeApplication(Context); Running = true; @@ -666,11 +527,48 @@ WinMain ( RenderBuffer.ViewHeight = MainWindow.Height; Context.DeltaTime = LastFrameSecondsElapsed; - Context.UpdateAndRender(&Context, InputQueue, &RenderBuffer); + Context.UpdateAndRender(&Context, InputQueue, &RenderBuffer, &OutputData); RenderCommandBuffer(RenderBuffer); ClearRenderBuffer(&RenderBuffer); + if (true) + { + // NOTE(pjs): Send the network data + + // TODO(pjs): This should happen on another thread + /* + Saved this lien as an example of pushing onto a queue + Context->GeneralWorkQueue->PushWorkOnQueue(Context->GeneralWorkQueue, SACNSendDMXBufferListJob, Job, "SACN Send Data Job"); + */ + + for (addressed_data_buffer* BufferAt = OutputData.Root; + BufferAt != 0; + BufferAt = BufferAt->Next) + { + switch(BufferAt->AddressType) + { + case AddressType_NetworkIP: + { + Win32Socket_SendTo(BufferAt->SendSocket, + BufferAt->V4SendAddress, + BufferAt->SendPort, + (const char*)BufferAt->Memory, + BufferAt->MemorySize, + 0); + }break; + + case AddressType_ComPort: + { + HANDLE SerialPort = Win32SerialArray_GetOrOpen(BufferAt->ComPort, 9600, 8, 0, 1); + Win32SerialPort_Write(SerialPort, BufferAt->Data); + }break; + + InvalidDefaultCase; + } + } + } + Context.Mouse.LeftButtonState = GetMouseButtonStateAdvanced(Context.Mouse.LeftButtonState); Context.Mouse.MiddleButtonState = GetMouseButtonStateAdvanced(Context.Mouse.MiddleButtonState); Context.Mouse.RightButtonState = GetMouseButtonStateAdvanced(Context.Mouse.RightButtonState); @@ -729,6 +627,11 @@ WinMain ( Context.CleanupApplication(Context); + for (s32 SocketIdx = 0; SocketIdx < Win32Sockets.Count; SocketIdx++) + { + Win32Socket_Close(Win32Sockets.Values + SocketIdx); + } + s32 CleanupResult = 0; do { CleanupResult = WSACleanup(); diff --git a/src/app/platform_win32/win32_foldhaus_serial.h b/src/app/platform_win32/win32_foldhaus_serial.h index 31f4d73..c5e356a 100644 --- a/src/app/platform_win32/win32_foldhaus_serial.h +++ b/src/app/platform_win32/win32_foldhaus_serial.h @@ -5,6 +5,11 @@ // #ifndef WIN32_SERIAL_H +global u32 Win32SerialHandlesCountMax; +global u32 Win32SerialHandlesCount; +global HANDLE* Win32SerialHandles; +global gs_string* Win32SerialPortNames; + DCB Win32SerialPort_GetState(HANDLE ComPortHandle) { @@ -44,7 +49,26 @@ Win32SerialPort_Open(char* PortName) 0, // Not overlapped I/O NULL); - if (ComPortHandle == INVALID_HANDLE_VALUE) + if (ComPortHandle != INVALID_HANDLE_VALUE) + { + COMMTIMEOUTS Timeouts = { 0 }; + Timeouts.ReadIntervalTimeout = 50; // in milliseconds + Timeouts.ReadTotalTimeoutConstant = 50; // in milliseconds + Timeouts.ReadTotalTimeoutMultiplier = 10; // in milliseconds + Timeouts.WriteTotalTimeoutConstant = 50; // in milliseconds + Timeouts.WriteTotalTimeoutMultiplier = 10; // in milliseconds + + if (SetCommTimeouts(ComPortHandle, &Timeouts)) + { + + } + else + { + s32 Error = GetLastError(); + InvalidCodePath; + } + } + else { // Error s32 Error = GetLastError(); @@ -63,6 +87,8 @@ Win32SerialPort_Close(HANDLE PortHandle) void Win32SerialPort_Write(HANDLE PortHandle, gs_data Buffer) { + Assert(PortHandle != INVALID_HANDLE_VALUE); + DWORD BytesWritten = 0; if (WriteFile(PortHandle, Buffer.Memory, Buffer.Size, &BytesWritten, NULL)) { @@ -74,8 +100,64 @@ Win32SerialPort_Write(HANDLE PortHandle, gs_data Buffer) else { OutputDebugStringA("Error: Unable to write to port\n"); + s32 Error = GetLastError(); + //InvalidCodePath; } } +///////////////////////// +// Win32SerialArray + +void +Win32SerialArray_Create(gs_thread_context Context) +{ + Win32SerialHandlesCountMax = 32; + Win32SerialHandlesCount = 0; + Win32SerialHandles = AllocatorAllocArray(Context.Allocator, HANDLE, Win32SerialHandlesCountMax); + Win32SerialPortNames = AllocatorAllocArray(Context.Allocator, gs_string, Win32SerialHandlesCountMax); + for (u32 i = 0; i < Win32SerialHandlesCountMax; i++) + { + Win32SerialPortNames[i] = AllocatorAllocString(Context.Allocator, 256); + } +} + +void +Win32SerialArray_Push(HANDLE SerialHandle, gs_const_string PortName) +{ + Assert(Win32SerialHandlesCount < Win32SerialHandlesCountMax); + u32 Index = Win32SerialHandlesCount++; + Win32SerialHandles[Index] = SerialHandle; + PrintF(&Win32SerialPortNames[Index], "%S", PortName); +} + +HANDLE +Win32SerialArray_Get(gs_const_string PortName) +{ + HANDLE PortHandle = INVALID_HANDLE_VALUE; + for (u32 i = 0; i < Win32SerialHandlesCount; i++) + { + if (StringsEqual(Win32SerialPortNames[i].ConstString, PortName)) + { + PortHandle = Win32SerialHandles[i]; + break; + } + } + return PortHandle; +} + +HANDLE +Win32SerialArray_GetOrOpen(gs_const_string PortName, u32 BaudRate, u8 ByteSize, u8 Parity, u8 StopBits) +{ + HANDLE PortHandle = Win32SerialArray_Get(PortName); + if (PortHandle == INVALID_HANDLE_VALUE) + { + Assert(IsNullTerminated(PortName)); + PortHandle = Win32SerialPort_Open(PortName.Str); + Win32SerialPort_SetState(PortHandle, BaudRate, ByteSize, Parity, StopBits); + Win32SerialArray_Push(PortHandle, PortName); + } + return PortHandle; +} + #define WIN32_SERIAL_H #endif // WIN32_SERIAL_H \ No newline at end of file diff --git a/src/app/platform_win32/win32_foldhaus_socket.h b/src/app/platform_win32/win32_foldhaus_socket.h new file mode 100644 index 0000000..18df318 --- /dev/null +++ b/src/app/platform_win32/win32_foldhaus_socket.h @@ -0,0 +1,140 @@ +// +// File: win32_foldhaus_socket.h +// Author: Peter Slattery +// Creation Date: 2020-10-03 +// +#ifndef WIN32_FOLDHAUS_SOCKET_H + +struct win32_socket +{ + SOCKET Socket; +}; + +struct win32_socket_array +{ + win32_socket* Values; + s32 CountMax; + s32 Count; +}; + +////////////////////// +// +// Win32 Socket Array + +internal win32_socket_array +Win32SocketArray_Create(u32 CountMax, gs_memory_arena* Storage) +{ + win32_socket_array Result = {}; + Result.CountMax = CountMax; + Result.Values = PushArray(Storage, win32_socket, CountMax); + return Result; +} + +internal s32 +Win32SocketArray_Take(win32_socket_array* Array) +{ + Assert(Array->Count < Array->CountMax); + s32 Result = Array->Count++; + win32_socket* Socket = Array->Values + Result; + *Socket = {0}; + return Result; +} + +internal win32_socket* +Win32SocketArray_Get(win32_socket_array Array, s32 Index) +{ + Assert(Index < Array.Count); + win32_socket* Result = Array.Values + Index; + return Result; +} + +////////////////////// +// +// Win32 Socket System + +global win32_socket_array Win32Sockets; + +internal s32 +Win32Socket_SetOption(win32_socket* Socket, s32 Level, s32 Option, const char* OptionValue, s32 OptionLength) +{ + int Error = setsockopt(Socket->Socket, Level, Option, OptionValue, OptionLength); + if (Error == SOCKET_ERROR) + { + Error = WSAGetLastError(); + // TODO(Peter): :ErrorLogging + } + + return Error; +} + +internal s32 +Win32Socket_SetOption(platform_socket_handle SocketHandle, s32 Level, s32 Option, const char* OptionValue, s32 OptionLength) +{ + win32_socket* Socket = Win32SocketArray_Get(Win32Sockets, (s32)SocketHandle); + return Win32Socket_SetOption(Socket, Level, Option, OptionValue, OptionLength); +} + +PLATFORM_GET_SOCKET_HANDLE(Win32GetSocketHandle) +{ + // NOTE(Peter): These used to be passed in as paramters, but we only use this function + // with AF_INET, SOCK_DGRAM, and Protocol = 0. These are also platform specific values + // so I was having to include windows.h in the platform agnostic code to accomodate that + // function signature. + s32 AddressFamily = AF_INET; + s32 Type = SOCK_DGRAM; + s32 Protocol = 0; + + s32 Result = Win32SocketArray_Take(&Win32Sockets); + win32_socket* Socket = Win32SocketArray_Get(Win32Sockets, Result); + Socket->Socket = socket(AddressFamily, Type, Protocol); + if (Socket->Socket != INVALID_SOCKET) + { + int Error = Win32Socket_SetOption(Socket, IPPROTO_IP, IP_MULTICAST_TTL, + (const char*)(&Multicast_TimeToLive), sizeof(Multicast_TimeToLive)); + } + else + { + s32 Error = WSAGetLastError(); + InvalidCodePath; + } + + return (platform_socket_handle)Result; +} + +internal s32 +Win32Socket_SendTo(platform_socket_handle SocketHandle, u32 Address, u32 Port, const char* Buffer, s32 BufferLength, s32 Flags) +{ + win32_socket* Socket = Win32SocketArray_Get(Win32Sockets, (s32)SocketHandle); + + sockaddr_in SockAddress = {}; + SockAddress.sin_family = AF_INET; + SockAddress.sin_port = HostToNetU16(Port); + SockAddress.sin_addr.s_addr = HostToNetU32(Address); + + s32 LengthSent = sendto(Socket->Socket, Buffer, BufferLength, Flags, (sockaddr*)&SockAddress, sizeof(sockaddr_in)); + + if (LengthSent == SOCKET_ERROR) + { + s32 Error = WSAGetLastError(); + if (Error == 10051) + { + } + else + { + // TODO(Peter): :ErrorLogging + InvalidCodePath; + } + } + + return LengthSent; +} + +internal void +Win32Socket_Close(win32_socket* Socket) +{ + closesocket(Socket->Socket); + Socket->Socket = INVALID_SOCKET; +} + +#define WIN32_FOLDHAUS_SOCKET_H +#endif // WIN32_FOLDHAUS_SOCKET_H \ No newline at end of file diff --git a/src/gs_libs/gs_types.cpp b/src/gs_libs/gs_types.cpp index a86c01c..4db796a 100644 --- a/src/gs_libs/gs_types.cpp +++ b/src/gs_libs/gs_types.cpp @@ -2235,6 +2235,9 @@ PushSizeOnCursor_(gs_memory_cursor* Cursor, u64 Size, char* Location) #define PushStructOnCursor(cursor,type) (type*)PushSizeOnCursor_((cursor), sizeof(type), FileNameAndLineNumberString).Memory #define PushArrayOnCursor(cursor,type,count) (type*)PushSizeOnCursor_((cursor), sizeof(type) * (count), FileNameAndLineNumberString).Memory +#define MemoryCursor_WriteValue(cursor, type, value) *PushStructOnCursor(cursor, type) = value +#define MemoryCursor_WriteBuffer(cursor, buf, len) CopyMemoryTo((u8*)(buf), PushArrayOnCursor((cursor), u8, (len)), (len)) + internal void PopSizeOnCursor(gs_memory_cursor* Cursor, u64 Size) {