diff --git a/NightLight.Core/Core.fs b/NightLight.Core/Core.fs index b78a026..9d233f2 100644 --- a/NightLight.Core/Core.fs +++ b/NightLight.Core/Core.fs @@ -19,15 +19,20 @@ let internal generateZigbeeCommandToFixLight partOfDay light = type Event = | ReceivedZigbeeEvent of payload: string - | PartOfDayChanged + | PartOfDayChanged of newPartOfDay: PartOfDay -let onEventReceived (partOfDay: PartOfDay) (event: Event) = +type State = { PartOfDay: PartOfDay } + +let onEventReceived (state: State) (event: Event) = result { + let partOfDay = state.PartOfDay + match event with | ReceivedZigbeeEvent payload -> let! zigbeeEvent = parseZigbeeEvent payload return + state, match zigbeeEvent with | DeviceAnnounce friendlyName -> let maybeLight = tryFindLight friendlyName @@ -35,5 +40,6 @@ let onEventReceived (partOfDay: PartOfDay) (event: Event) = match maybeLight with | Some light -> generateZigbeeCommandToFixLight partOfDay light |> Seq.singleton | None -> Seq.empty - | PartOfDayChanged -> return lights |> Seq.map (generateZigbeeCommandToFixLight partOfDay) + | PartOfDayChanged newPartOfDay -> + return { PartOfDay = newPartOfDay }, lights |> Seq.map (generateZigbeeCommandToFixLight partOfDay) } diff --git a/NightLight/Program.fs b/NightLight/Program.fs index 783678f..6c9121b 100644 --- a/NightLight/Program.fs +++ b/NightLight/Program.fs @@ -1,5 +1,6 @@ open System open System.Text +open System.Threading open System.Threading.Tasks open Microsoft.Extensions.Logging open MQTTnet @@ -35,24 +36,32 @@ let private publishZigbeeCommands (mqttClient: IMqttClient) (logger: ILogger) (c |> Async.Ignore } -let private handleEvent (mqttClient: IMqttClient) (logger: ILogger) (partOfDay: PartOfDay) (event: Event) = - let commandsResult = event |> onEventReceived partOfDay +let private handleEvent (mqttClient: IMqttClient) (logger: ILogger) (state: State) (event: Event) = + let result = event |> onEventReceived state - match commandsResult with - | Ok commands -> publishZigbeeCommands mqttClient logger commands - | Error UnknownType -> async.Return() + match result with + | Ok(newState, commands) -> + async { + do! publishZigbeeCommands mqttClient logger commands + return newState + } + | Error UnknownType -> async.Return state | Error e -> logger.LogError("Error {Error} while {Event}", e, event) - async.Return() + async.Return state -let private onMqttMessageReceived (mqttClient: IMqttClient) (logger: ILogger) (message: MqttApplicationMessage) = +let private onMqttMessageReceived + (mqttClient: IMqttClient) + (logger: ILogger) + (state: State) + (message: MqttApplicationMessage) + = let payload = message.Payload let decodedPayload = Encoding.UTF8.GetString(&payload) logger.LogInformation("Received message with payload {Payload}", decodedPayload) - ReceivedZigbeeEvent decodedPayload - |> handleEvent mqttClient logger (getPartOfDay DateTime.Now) + ReceivedZigbeeEvent decodedPayload |> handleEvent mqttClient logger state [] let mainAsync _ = @@ -76,8 +85,19 @@ let mainAsync _ = let mqttClientOptions = MqttClientOptionsBuilder().WithTcpServer(server).Build() + let stateLock = new SemaphoreSlim(1, 1) + let mutable state = { PartOfDay = getPartOfDay DateTime.Now } + mqttClient.add_ApplicationMessageReceivedAsync (fun e -> - onMqttMessageReceived mqttClient logger e.ApplicationMessage + async { + do! stateLock.WaitAsync() |> Async.AwaitTask + + try + let! newState = onMqttMessageReceived mqttClient logger state e.ApplicationMessage + state <- newState + finally + stateLock.Release() |> ignore + } |> Async.StartAsTask :> Task) @@ -94,7 +114,13 @@ let mainAsync _ = let currentPartOfDay = getPartOfDay DateTime.Now if previousPartOfDay <> Some currentPartOfDay then - do! PartOfDayChanged |> handleEvent mqttClient logger currentPartOfDay + do! stateLock.WaitAsync() |> Async.AwaitTask + + try + let! newState = PartOfDayChanged currentPartOfDay |> handleEvent mqttClient logger state + state <- newState + finally + stateLock.Release() |> ignore previousPartOfDay <- Some currentPartOfDay