491 lines
19 KiB
C++
491 lines
19 KiB
C++
//
|
|
// File: blumen_lumen.cpp
|
|
// Author: Peter Slattery
|
|
// Creation Date: 2021-01-23
|
|
//
|
|
#ifndef BLUMEN_LUMEN_CPP
|
|
|
|
internal bool
|
|
MessageQueue_CanRead(blumen_network_msg_queue* Queue)
|
|
{
|
|
bool Result = (Queue->ReadHead != Queue->WriteHead);
|
|
return Result;
|
|
}
|
|
|
|
internal gs_data
|
|
MessageQueue_Read(blumen_network_msg_queue* Queue)
|
|
{
|
|
gs_data Result = {};
|
|
u32 ReadIndex = Queue->ReadHead++;
|
|
if (Queue->ReadHead >= BLUMEN_MESSAGE_QUEUE_COUNT)
|
|
{
|
|
Queue->ReadHead = 0;
|
|
}
|
|
Result = Queue->Buffers[ReadIndex];
|
|
return Result;
|
|
}
|
|
|
|
// KB(1) is just bigger than any packet we send. Good for now
|
|
#define DEFAULT_QUEUE_ENTRY_SIZE KB(1)
|
|
|
|
internal void
|
|
MessageQueue_Init(blumen_network_msg_queue* Queue, gs_memory_arena* Arena)
|
|
{
|
|
for (u32 i = 0; i < BLUMEN_MESSAGE_QUEUE_COUNT; i++)
|
|
{
|
|
Queue->Buffers[i] = PushSizeToData(Arena, DEFAULT_QUEUE_ENTRY_SIZE);
|
|
}
|
|
}
|
|
|
|
internal bool
|
|
MessageQueue_Write(blumen_network_msg_queue* Queue, gs_data Msg)
|
|
{
|
|
Assert(Msg.Size <= DEFAULT_QUEUE_ENTRY_SIZE);
|
|
|
|
u32 Index = Queue->WriteHead;
|
|
Assert(Index >= 0 &&
|
|
Index < BLUMEN_MESSAGE_QUEUE_COUNT);
|
|
|
|
gs_data* Dest = Queue->Buffers + Index;
|
|
CopyMemoryTo(Msg.Memory, Dest->Memory, Msg.Size);
|
|
Dest->Size = Msg.Size;
|
|
|
|
// NOTE(pjs): We increment write head at the end of writing so that
|
|
// a reader thread doesn't pull the message off before we've finished
|
|
// filling it out
|
|
Queue->WriteHead = (Queue->WriteHead + 1) % BLUMEN_MESSAGE_QUEUE_COUNT;
|
|
return true;
|
|
}
|
|
|
|
internal bool
|
|
MessageQueue_CanWrite(blumen_network_msg_queue Queue)
|
|
{
|
|
bool Result = ((Queue.WriteHead >= Queue.ReadHead) ||
|
|
(Queue.WriteHead < Queue.ReadHead));
|
|
return Result;
|
|
}
|
|
|
|
internal void
|
|
BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData)
|
|
{
|
|
mic_listen_job_data* Data = (mic_listen_job_data*)UserData;
|
|
|
|
gs_data Msg = {};
|
|
|
|
u8 WeathermanIPAddr[4] = {};
|
|
WeathermanIPAddr[0] = 127;
|
|
WeathermanIPAddr[1] = 0;
|
|
WeathermanIPAddr[2] = 0;
|
|
WeathermanIPAddr[3] = 1;
|
|
|
|
u32 WeathermanIPV4 = (u32)UpackB4(WeathermanIPAddr);
|
|
u32 WeathermanPort = 20185;
|
|
|
|
while (*Data->Running)
|
|
{
|
|
if (SocketQueryStatus(Data->SocketManager, Data->ListenSocket))
|
|
{
|
|
if (SocketPeek(Data->SocketManager, Data->ListenSocket))
|
|
{
|
|
// TODO(pjs): Make this a peek operation
|
|
Msg = SocketRecieve(Data->SocketManager, Data->ListenSocket, Ctx->Transient);
|
|
if (Msg.Size > 0)
|
|
{
|
|
MessageQueue_Write(Data->IncomingMsgQueue, Msg);
|
|
}
|
|
}
|
|
|
|
while (MessageQueue_CanRead(Data->OutgoingMsgQueue))
|
|
{
|
|
Msg = MessageQueue_Read(Data->OutgoingMsgQueue);
|
|
u32 Address = WeathermanIPV4;
|
|
u32 Port = WeathermanPort;
|
|
s32 Flags = 0;
|
|
SocketSend(Data->SocketManager, Data->ListenSocket, Address, Port, Msg, Flags);
|
|
}
|
|
}
|
|
}
|
|
|
|
CloseSocket(Data->SocketManager, Data->ListenSocket);
|
|
}
|
|
|
|
internal void
|
|
BlumenLumen_LoadPatterns(app_state* State)
|
|
{
|
|
animation_pattern_array* Patterns = &State->Patterns;
|
|
if (Patterns->CountMax == 0)
|
|
{
|
|
*Patterns = Patterns_Create(&State->Permanent, 32);
|
|
}
|
|
|
|
Patterns->Count = 0;
|
|
Patterns_PushPattern(Patterns, TestPatternOne, PATTERN_MULTITHREADED);
|
|
Patterns_PushPattern(Patterns, TestPatternTwo, PATTERN_MULTITHREADED);
|
|
Patterns_PushPattern(Patterns, TestPatternThree, PATTERN_MULTITHREADED);
|
|
Patterns_PushPattern(Patterns, Pattern_AllGreen, PATTERN_MULTITHREADED);
|
|
Patterns_PushPattern(Patterns, Pattern_HueShift, PATTERN_MULTITHREADED);
|
|
Patterns_PushPattern(Patterns, Pattern_HueFade, PATTERN_MULTITHREADED);
|
|
Patterns_PushPattern(Patterns, Pattern_Spots, PATTERN_MULTITHREADED);
|
|
Patterns_PushPattern(Patterns, Pattern_LighthouseRainbow, PATTERN_MULTITHREADED);
|
|
Patterns_PushPattern(Patterns, Pattern_SmoothGrowRainbow, PATTERN_MULTITHREADED);
|
|
Patterns_PushPattern(Patterns, Pattern_GrowAndFade, PATTERN_MULTITHREADED);
|
|
Patterns_PushPattern(Patterns, Pattern_ColorToWhite, PATTERN_MULTITHREADED);
|
|
Patterns_PushPattern(Patterns, Pattern_Blue, PATTERN_MULTITHREADED);
|
|
Patterns_PushPattern(Patterns, Pattern_Green, PATTERN_MULTITHREADED);
|
|
Patterns_PushPattern(Patterns, Pattern_FlowerColors, PATTERN_MULTITHREADED);
|
|
Patterns_PushPattern(Patterns, Pattern_FlowerColorToWhite, PATTERN_MULTITHREADED);
|
|
Patterns_PushPattern(Patterns, Pattern_BasicFlowers, PATTERN_MULTITHREADED);
|
|
// 15
|
|
Patterns_PushPattern(Patterns, Pattern_Wavy, PATTERN_MULTITHREADED);
|
|
Patterns_PushPattern(Patterns, Pattern_Patchy, PATTERN_MULTITHREADED);
|
|
Patterns_PushPattern(Patterns, Pattern_Leafy, PATTERN_MULTITHREADED);
|
|
Patterns_PushPattern(Patterns, Pattern_LeafyPatchy, PATTERN_MULTITHREADED);
|
|
Patterns_PushPattern(Patterns, Pattern_WavyPatchy, PATTERN_SINGLETHREADED);
|
|
}
|
|
|
|
internal v4
|
|
TEMP_Saturate(v4 P)
|
|
{
|
|
v4 CRGB = P;
|
|
v4 CHSV = RGBToHSV(CRGB);
|
|
if (CHSV.g > .3f)
|
|
{
|
|
CHSV.g = 1;
|
|
CRGB = HSVToRGB(CHSV);
|
|
}
|
|
return CRGB;
|
|
}
|
|
|
|
internal gs_data
|
|
BlumenLumen_CustomInit(app_state* State, context Context)
|
|
{
|
|
// This is memory for any custom data that we want to use
|
|
// as a part of a particular sculpture.
|
|
// By returning it from here, it will be sent as an argument to
|
|
// the sculpture's CustomUpdate function;
|
|
gs_data Result = {};
|
|
|
|
Result = PushSizeToData(&State->Permanent, sizeof(blumen_lumen_state));
|
|
|
|
blumen_lumen_state* BLState = (blumen_lumen_state*)Result.Memory;
|
|
BLState->Running = true;
|
|
BLState->BrightnessPercent = 1;
|
|
MessageQueue_Init(&BLState->IncomingMsgQueue, &State->Permanent);
|
|
MessageQueue_Init(&BLState->OutgoingMsgQueue, &State->Permanent);
|
|
|
|
BLState->MicListenJobData.Running = &BLState->Running;
|
|
BLState->MicListenJobData.SocketManager = Context.SocketManager;
|
|
BLState->MicListenJobData.IncomingMsgQueue = &BLState->IncomingMsgQueue;
|
|
BLState->MicListenJobData.OutgoingMsgQueue = &BLState->OutgoingMsgQueue;
|
|
BLState->MicListenJobData.ListenSocket = CreateSocket(Context.SocketManager, "127.0.0.1", "20185");
|
|
|
|
#if 0
|
|
BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData);
|
|
#endif
|
|
|
|
gs_const_string SculpturePath = ConstString("data/test_blumen.fold");
|
|
LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath, State->GlobalLog);
|
|
|
|
#if 0
|
|
{ // Animation PLAYGROUND
|
|
animation_desc Desc = {};
|
|
Desc.NameSize = 256;
|
|
Desc.LayersCount = 8;
|
|
Desc.BlocksCount = 8;
|
|
Desc.MinFrames = 0;
|
|
Desc.MaxFrames = SecondsToFrames(15, State->AnimationSystem);
|
|
|
|
animation_desc Desc0 = Desc;
|
|
Desc.Name = "test_anim_zero";
|
|
animation Anim0 = Animation_Create(Desc0, &State->AnimationSystem);
|
|
Animation_AddLayer(&Anim0, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem);
|
|
Animation_AddBlock(&Anim0, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(15), 0);
|
|
BLState->AnimHandles[0] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim0);
|
|
|
|
animation_desc Desc1 = Desc;
|
|
Desc1.Name = "test_anim_one";
|
|
animation Anim1 = Animation_Create(Desc1, &State->AnimationSystem);
|
|
Animation_AddLayer(&Anim1, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem);
|
|
Animation_AddBlock(&Anim1, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(12), 0);
|
|
BLState->AnimHandles[1] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim1);
|
|
|
|
animation_desc Desc2 = Desc;
|
|
Desc2.Name = "i_love_you";
|
|
animation Anim2 = Animation_Create(Desc2, &State->AnimationSystem);;
|
|
Animation_AddLayer(&Anim2, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem);
|
|
Animation_AddBlock(&Anim2, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(20), 0);
|
|
BLState->AnimHandles[2] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim2);
|
|
|
|
State->AnimationSystem.ActiveFadeGroup.From = BLState->AnimHandles[2];
|
|
} // End Animation Playground
|
|
#endif
|
|
animation_handle DemoPatternsAnim = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem,
|
|
State->Patterns,
|
|
Context,
|
|
ConstString("data/demo_patterns.foldanim"));
|
|
State->AnimationSystem.ActiveFadeGroup.From = DemoPatternsAnim;
|
|
State->AnimationSystem.TimelineShouldAdvance = true;
|
|
|
|
for (u32 i = 0; i < FLOWER_COLORS_COUNT; i++)
|
|
{
|
|
//FlowerAColors[i] = TEMP_Saturate(FlowerAColors[i]);
|
|
//FlowerBColors[i] = TEMP_Saturate(FlowerBColors[i]);
|
|
//FlowerCColors[i] = TEMP_Saturate(FlowerCColors[i]);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
internal void
|
|
BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context)
|
|
{
|
|
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory;
|
|
|
|
bool SendMotorCommand = false;
|
|
blumen_packet MotorCommand = {};
|
|
|
|
#if 0
|
|
MotorTimeElapsed += Context->DeltaTime;
|
|
BLState->TimeElapsed += Context->DeltaTime;
|
|
|
|
if (BLState->TimeElapsed > 5)
|
|
{
|
|
u32 NextIndex = ++BLState->CurrAnim % 3;
|
|
animation_handle Next = BLState->AnimHandles[NextIndex];
|
|
AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, Next, 5);
|
|
BLState->TimeElapsed = 0;
|
|
}
|
|
#endif
|
|
|
|
while (MessageQueue_CanRead(&BLState->IncomingMsgQueue))
|
|
{
|
|
gs_data PacketData = MessageQueue_Read(&BLState->IncomingMsgQueue);
|
|
|
|
blumen_packet Packet = *(blumen_packet*)PacketData.Memory;
|
|
switch (Packet.Type) {
|
|
case PacketType_PatternCommand:
|
|
{
|
|
microphone_packet Mic = Packet.MicPacket;
|
|
u32 NameLen = CStringLength(Mic.AnimationFileName);
|
|
|
|
for (u32 i = 0; i < PhraseToAnimMapCount; i++)
|
|
{
|
|
gs_const_string PhraseStr = ConstString(PhraseToAnimMap[i].Phrase);
|
|
u32 PhraseIndex = PhraseToAnimMap[i].PatternIndex;
|
|
if (StringEqualsCharArray(PhraseStr, Mic.AnimationFileName, NameLen))
|
|
{
|
|
AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup,
|
|
animation_handle{(s32)PhraseIndex},
|
|
3.0f);
|
|
OutputDebugStringA("\nReceived Pattern Packet\n");
|
|
|
|
{
|
|
// DEBUG CODE
|
|
u8 MotorState = BLState->LastKnownMotorState.FlowerPositions[0];
|
|
if (MotorState == 2) {
|
|
OutputDebugStringA("Sending 1\n");
|
|
MotorState = 1;
|
|
}
|
|
else
|
|
{
|
|
OutputDebugStringA("Sending 1\n");
|
|
MotorState = 2;
|
|
}
|
|
|
|
blumen_packet MPacket = {};
|
|
MPacket.Type = PacketType_MotorState;
|
|
MPacket.MotorPacket.FlowerPositions[0] = MotorState;
|
|
MPacket.MotorPacket.FlowerPositions[1] = MotorState;
|
|
MPacket.MotorPacket.FlowerPositions[2] = MotorState;
|
|
MotorCommand = MPacket;
|
|
SendMotorCommand = true;
|
|
}
|
|
}
|
|
}
|
|
}break;
|
|
|
|
case PacketType_MotorState:
|
|
{
|
|
motor_status_packet Motor = Packet.MotorStatusPacket;
|
|
|
|
// NOTE(pjs): Python sends multi-byte integers in little endian
|
|
// order. Have to unpack
|
|
u8* T = (u8*)&Motor.Temperature;
|
|
Motor.Temperature = (T[0] << 8 |
|
|
T[1] << 0);
|
|
|
|
BLState->LastKnownMotorState = Motor.Pos;
|
|
|
|
gs_string Temp = PushStringF(State->Transient, 256, "\nReceived Motor States: \n\tPos: %d %d %d\n\tErr: %d %d %d\n\tTemp: %d\n\n",
|
|
Motor.Pos.FlowerPositions[0],
|
|
Motor.Pos.FlowerPositions[1],
|
|
Motor.Pos.FlowerPositions[2],
|
|
Motor.MotorStatus[0],
|
|
Motor.MotorStatus[1],
|
|
Motor.MotorStatus[2],
|
|
(u32)Motor.Temperature
|
|
);
|
|
NullTerminate(&Temp);
|
|
|
|
OutputDebugStringA(Temp.Str);
|
|
}break;
|
|
|
|
case PacketType_Temperature:
|
|
{
|
|
temp_packet Temp = Packet.TempPacket;
|
|
|
|
if (Temp.Temperature > 21)
|
|
{
|
|
BLState->BrightnessPercent = .25f;
|
|
}
|
|
else
|
|
{
|
|
BLState->BrightnessPercent = 1.f;
|
|
}
|
|
|
|
gs_string TempStr = PushStringF(State->Transient, 256, "\nTemperature: %d\n",
|
|
Temp.Temperature);
|
|
NullTerminate(&TempStr);
|
|
OutputDebugStringA(TempStr.Str);
|
|
}break;
|
|
|
|
InvalidDefaultCase;
|
|
}
|
|
}
|
|
|
|
|
|
// Open / Close the Motor
|
|
|
|
if (MessageQueue_CanWrite(BLState->OutgoingMsgQueue))
|
|
{
|
|
#if 0
|
|
for (u32 i = 0; i < MotorOpenTimesCount; i++)
|
|
{
|
|
time_range Range = MotorOpenTimes[i];
|
|
|
|
bool CurrTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Current, Range);
|
|
|
|
bool LastTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Last, Range);
|
|
|
|
bool SendOpen = CurrTimeInRange && !LastTimeInRange;
|
|
|
|
r64 NanosSinceLastSend = ((r64)Context->SystemTime_Current.NanosSinceEpoch - (r64)BLState->LastSendTime.NanosSinceEpoch);
|
|
r64 SecondsSinceLastSend = NanosSinceLastSend / PowR32(10, 8);
|
|
|
|
//SendOpen = SecondsSinceLastSend > 2;
|
|
if (SendOpen)
|
|
{
|
|
SendMotorCommand = true;
|
|
|
|
BLState->LastSendTime = Context->SystemTime_Current;
|
|
OutputDebugString("Motors: Open\n");
|
|
|
|
blumen_packet Packet = {};
|
|
Packet.Type = PacketType_MotorState;
|
|
Packet.MotorPacket.FlowerPositions[0] = 2;
|
|
Packet.MotorPacket.FlowerPositions[1] = 2;
|
|
Packet.MotorPacket.FlowerPositions[2] = 2;
|
|
MotorCommand = Packet;
|
|
}
|
|
else if (!CurrTimeInRange && LastTimeInRange)
|
|
{
|
|
SendMotorCommand = true;
|
|
OutputDebugString("Motors: Close\n");
|
|
|
|
blumen_packet Packet = {};
|
|
Packet.Type = PacketType_MotorState;
|
|
Packet.MotorPacket.FlowerPositions[0] = 1;
|
|
Packet.MotorPacket.FlowerPositions[1] = 1;
|
|
Packet.MotorPacket.FlowerPositions[2] = 1;
|
|
MotorCommand = Packet;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (SendMotorCommand)
|
|
{
|
|
gs_data Msg = StructToData(&MotorCommand, blumen_packet);
|
|
MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg);
|
|
}
|
|
}
|
|
// Dim the leds based on temp data
|
|
#define DIM_LED_BRIGHTNESS 1
|
|
#if DIM_LED_BRIGHTNESS
|
|
for (u32 i = 0; i < State->LedSystem.BuffersCount; i++)
|
|
{
|
|
led_buffer Buffer = State->LedSystem.Buffers[i];
|
|
for (u32 j = 0; j < Buffer.LedCount; j++)
|
|
{
|
|
pixel* Color = Buffer.Colors + j;
|
|
Color->R = Color->R * BLState->BrightnessPercent;
|
|
Color->G = Color->G * BLState->BrightnessPercent;
|
|
Color->B = Color->B * BLState->BrightnessPercent;
|
|
}
|
|
}
|
|
|
|
// TODO(PS): This should really only happen if we think the
|
|
// flower _might_ be open
|
|
for (u32 a = 0; a < State->Assemblies.Count; a++)
|
|
{
|
|
assembly Assembly = State->Assemblies.Values[a];
|
|
led_buffer Buffer = State->LedSystem.Buffers[Assembly.LedBufferIndex];
|
|
|
|
led_strip_list TopStrips = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("inner_bloom"), State->Transient);
|
|
for (u32 s = 0; s < TopStrips.Count; s++)
|
|
{
|
|
u32 SIndex = TopStrips.StripIndices[s];
|
|
v2_strip Strip = Assembly.Strips[SIndex];
|
|
for (u32 l = 0; l < Strip.LedCount; l++)
|
|
{
|
|
u32 LIndex = Strip.LedLUT[l];
|
|
Buffer.Colors[LIndex] = {0};
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Send Status Packet
|
|
{
|
|
system_time LastSendTime = BLState->LastStatusUpdateTime;
|
|
r64 NanosSinceLastSend = ((r64)Context->SystemTime_Current.NanosSinceEpoch - (r64)LastSendTime.NanosSinceEpoch);
|
|
r64 SecondsSinceLastSend = NanosSinceLastSend / PowR32(10, 8);
|
|
if (SecondsSinceLastSend >= STATUS_PACKET_FREQ_SECONDS)
|
|
{
|
|
BLState->LastStatusUpdateTime = Context->SystemTime_Current;
|
|
OutputDebugString("Sending Status\n");
|
|
|
|
blumen_packet Packet = {};
|
|
Packet.Type = PacketType_LumenariumStatus;
|
|
Packet.StatusPacket.NextMotorEventType = 0;
|
|
Packet.StatusPacket.NextEventTime = 0;
|
|
|
|
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
|
|
CopyMemoryTo(ActiveAnim->Name.Str, Packet.StatusPacket.AnimFileName,
|
|
Min(ActiveAnim->Name.Length, 32));
|
|
Packet.StatusPacket.AnimFileName[ActiveAnim->Name.Length] = 0;
|
|
|
|
gs_data Msg = StructToData(&Packet, blumen_packet);
|
|
MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
US_CUSTOM_CLEANUP(BlumenLumen_CustomCleanup)
|
|
{
|
|
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory;
|
|
BLState->Running = false;
|
|
}
|
|
|
|
internal user_space_desc
|
|
BlumenLumen_UserSpaceCreate()
|
|
{
|
|
user_space_desc Result = {};
|
|
Result.LoadPatterns = BlumenLumen_LoadPatterns;
|
|
Result.CustomInit = BlumenLumen_CustomInit;
|
|
Result.CustomUpdate = BlumenLumen_CustomUpdate;
|
|
Result.CustomCleanup = BlumenLumen_CustomCleanup;
|
|
return Result;
|
|
}
|
|
|
|
#define BLUMEN_LUMEN_CPP
|
|
#endif // BLUMEN_LUMEN_CPP
|