Lumenarium/src/app/ss_blumen_lumen/blumen_lumen.cpp

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