Working process.call

This commit is contained in:
Mikko Ahlroth 2024-07-06 00:17:28 +03:00
parent 25deeec162
commit fede52dd60
9 changed files with 167 additions and 69 deletions

View file

@ -27,9 +27,11 @@ htmgrrrl = ">= 0.3.0 and < 1.0.0"
form_coder = ">= 0.3.0 and < 1.0.0"
birl = ">= 1.7.1 and < 2.0.0"
gleam_json = ">= 1.0.1 and < 2.0.0"
envoy = ">= 1.0.1 and < 2.0.0"
mist = ">= 1.2.0 and < 2.0.0"
aurinko_common = { path = "../common" }
gleamy_structures = ">= 1.0.0 and < 2.0.0"
glubsub = ">= 1.0.1 and < 2.0.0"
dot_env = ">= 1.0.0 and < 2.0.0"
chip = ">= 0.7.5 and < 1.0.0"
[dev-dependencies]

View file

@ -5,7 +5,8 @@ packages = [
{ name = "aurinko_common", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], source = "local", path = "../common" },
{ name = "birl", version = "1.7.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "5C66647D62BCB11FE327E7A6024907C4A17954EF22865FE0940B54A852446D01" },
{ name = "biscotto", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_http", "gleam_stdlib"], otp_app = "biscotto", source = "hex", outer_checksum = "A1CFEA1686FA8ABDE90B76E22775FF29EE8156A64DAC327F48141A68951D662C" },
{ name = "envoy", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "CFAACCCFC47654F7E8B75E614746ED924C65BD08B1DE21101548AC314A8B6A41" },
{ name = "chip", version = "0.7.5", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "chip", source = "hex", outer_checksum = "1C50EB65CE8617D64FBCCE9A3EA7E6C7BF3E646D8E39B92EA74F68B67476F59D" },
{ name = "dot_env", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "simplifile"], otp_app = "dot_env", source = "hex", outer_checksum = "E7B84DC7B579553AF3B9F0A03B2F8DDB9B44521F553CCFBE633AA595C27F1A05" },
{ name = "esqlite", version = "0.8.8", build_tools = ["rebar3"], requirements = [], otp_app = "esqlite", source = "hex", outer_checksum = "374902457C7D94DC9409C98D3BDD1CA0D50A60DC9F3BDF1FD8EB74C0DCDF02D6" },
{ name = "exception", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "F5580D584F16A20B7FCDCABF9E9BE9A2C1F6AC4F9176FA6DD0B63E3B20D450AA" },
{ name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" },
@ -19,6 +20,7 @@ packages = [
{ name = "gleam_stdlib", version = "0.38.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "663CF11861179AF415A625307447775C09404E752FF99A24E2057C835319F1BE" },
{ name = "gleamy_structures", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleamy_structures", source = "hex", outer_checksum = "FF1B7600123B2B7C15E91BEBE6F417B4003928BF4282EF01A74A792B4ED6985A" },
{ name = "glisten", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "glisten", source = "hex", outer_checksum = "CF3A9383E9BA4A8CBAF2F7B799716290D02F2AC34E7A77556B49376B662B9314" },
{ name = "glubsub", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "glubsub", source = "hex", outer_checksum = "82563C2A7B5354D58FA2BEA88EF021DEB0F22189043668A604902034F5E2E81D" },
{ name = "gramps", version = "2.0.3", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gramps", source = "hex", outer_checksum = "3CCAA6E081225180D95C79679D383BBF51C8D1FDC1B84DA1DA444F628C373793" },
{ name = "hpack_erl", version = "0.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "hpack", source = "hex", outer_checksum = "D6137D7079169D8C485C6962DFE261AF5B9EF60FBC557344511C1E65E3D95FB0" },
{ name = "htmerl", version = "0.1.0", build_tools = ["rebar3"], requirements = [], otp_app = "htmerl", source = "hex", outer_checksum = "D932F76EA33C318A79F41A429FDBEAE30820390DDB72BA89F38EEF32FEC36395" },
@ -38,7 +40,8 @@ packages = [
aurinko_common = { path = "../common" }
birl = { version = ">= 1.7.1 and < 2.0.0" }
biscotto = { version = ">= 1.0.0 and < 2.0.0" }
envoy = { version = ">= 1.0.1 and < 2.0.0" }
chip = { version = ">= 0.7.5 and < 1.0.0"}
dot_env = { version = ">= 1.0.0 and < 2.0.0" }
form_coder = { version = ">= 0.3.0 and < 1.0.0" }
gleam_erlang = { version = ">= 0.25.0 and < 1.0.0" }
gleam_http = { version = ">= 3.6.0 and < 4.0.0" }
@ -46,7 +49,8 @@ gleam_httpc = { version = ">= 2.2.0 and < 3.0.0" }
gleam_json = { version = ">= 1.0.1 and < 2.0.0" }
gleam_otp = { version = ">= 0.10.0 and < 1.0.0" }
gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" }
gleamy_structures = { version = ">= 1.0.0 and < 2.0.0"}
gleamy_structures = { version = ">= 1.0.0 and < 2.0.0" }
glubsub = { version = ">= 1.0.1 and < 2.0.0" }
htmgrrrl = { version = ">= 0.3.0 and < 1.0.0" }
lustre = { version = ">= 4.3.0 and < 5.0.0" }
mist = { version = ">= 1.2.0 and < 2.0.0" }

View file

@ -1,13 +1,14 @@
import birl
import biscotto
import form_coder
import gleam/bool
import gleam/dynamic
import gleam/float
import gleam/http
import gleam/http/request
import gleam/http/response
import gleam/httpc
import gleam/int
import gleam/io
import gleam/json
import gleam/list
import gleam/option
@ -19,6 +20,8 @@ import htmgrrrl
pub type APIError {
RequestError(err: dynamic.Dynamic)
DecodeError(err: json.DecodeError)
NoContentTypeError
ContentTypeError(wrong_content_type: String)
ScrapeError
}
@ -88,25 +91,37 @@ pub fn login(username: String, password: String) {
pub fn get_production(cookies: biscotto.CookieJar) {
let assert Ok(req) = request.from_uri(production_info_url())
use resp <- result.try(
let req =
req
|> request.set_method(http.Post)
|> set_common_headers()
|> biscotto.with_cookies(cookies)
use resp <- result.try(
req
|> httpc.send()
|> result.map_error(RequestError),
)
use content_type <- result.try(
response.get_header(resp, "content-type")
|> result.replace_error(NoContentTypeError),
)
use <- bool.guard(
!string.contains(content_type, "application/json"),
Error(ContentTypeError(content_type)),
)
json.decode(
resp.body,
dynamic.decode7(
ProductionInfo,
dynamic.field("co2", float_string_decoder),
dynamic.field("duration", dynamic.int),
dynamic.field("ecu_sign", dynamic.string),
dynamic.field("last_power", float_string_decoder),
dynamic.field("ecuSign", dynamic.string),
dynamic.field("lastPower", float_string_decoder),
dynamic.field("lifetime", float_string_decoder),
dynamic.field("meter_flag", dynamic.int),
dynamic.field("meterFlag", dynamic.int),
dynamic.field("today", float_string_decoder),
),
)
@ -215,7 +230,6 @@ pub fn get_module_power(cookies: biscotto.CookieJar, modules: ModuleIDs) {
)
|> set_common_headers()
|> biscotto.with_cookies(cookies)
|> io.debug()
|> httpc.send()
|> result.map_error(RequestError)
@ -271,8 +285,16 @@ fn module_power_url() {
fn float_string_decoder(str: dynamic.Dynamic) {
use value <- result.try(dynamic.string(str))
float.parse(value)
|> result.replace_error([
dynamic.DecodeError("a string representing a float", value, []),
])
case float.parse(value) {
Ok(f) -> Ok(f)
Error(_) ->
case int.parse(value) {
Ok(i) -> Ok(int.to_float(i))
Error(_) ->
Error([
dynamic.DecodeError("a string representing a float", value, []),
])
}
}
}

View file

@ -1,34 +0,0 @@
import ap_systems/api
import ap_systems/module_power
import aurinko/web
import envoy
import gleam/erlang/process
import gleam/int
import gleam/io
import gleam/result
import gleam/string
pub fn main() {
let assert Ok(username) = envoy.get("USERNAME")
let assert Ok(password) = envoy.get("PASSWORD")
let assert Ok(port) = envoy.get("PORT")
let assert Ok(port) = int.parse(port)
let assert Ok(secret_key_base) = envoy.get("SECRET_KEY_BASE")
let assert Ok(_) = web.init(port, secret_key_base)
process.sleep_forever()
// use cookies <- result.try(
// api.login(username, password) |> result.map_error(string.inspect),
// )
// io.debug(cookies)
// io.debug(api.get_production(cookies) |> result.map_error(string.inspect))
// use ids <- result.try(
// io.debug(api.get_module_ids(cookies)) |> result.map_error(string.inspect),
// )
// use module_data <- result.try(
// api.get_module_power(cookies, ids) |> result.map_error(string.inspect),
// )
// io.debug(module_power.decode_api_data(module_data))
// |> result.map_error(string.inspect)
}

View file

@ -1,5 +1,9 @@
import ap_systems/api
import ap_systems/module_power
import aurinko/updater/pubsub
import aurinko/updater/types.{
type Dataset, type ModuleDataset, Dataset, ModuleDataset, ModulePower,
}
import birl
import birl/duration
import biscotto
@ -13,37 +17,25 @@ import gleam/order
import gleam/otp/actor
import gleam/result
import gleamy/red_black_tree_set.{type Set}
import glubsub
const update_interval = 10_000
const login_cookie_expiry = 86_400
pub type GetDataResponse {
GetDataResponse(dataset: option.Option(Dataset))
}
pub type Message {
Update
PeriodicUpdate
GetData(reply_subject: process.Subject(GetDataResponse))
}
pub type ProductionInfo =
api.ProductionInfo
pub type ModuleIDs =
api.ModuleIDs
pub type MomentaryPower =
module_power.MomentaryPower
pub type ModulePower {
ModulePower(id: String, power: dict.Dict(birl.Time, MomentaryPower))
}
pub type ModuleDataset {
ModuleDataset(times: Set(birl.Time), datas: dict.Dict(String, ModulePower))
}
pub type Dataset {
Dataset(production_info: ProductionInfo, modules: ModuleDataset)
}
pub type Credentials {
Credentials(username: String, password: String)
}
@ -71,6 +63,7 @@ pub type State {
inner: InnerState,
error: option.Option(UpdateError),
self_subject: process.Subject(Message),
pubsub: pubsub.UpdaterPubSub,
)
}
@ -79,7 +72,11 @@ pub type UpdateError {
DecodeError(err: dynamic.DecodeError)
}
pub fn start(username: String, password: String) {
pub fn get_data(subject: process.Subject(Message)) {
process.call(subject, fn(reply_subject) { GetData(reply_subject) }, 5000)
}
pub fn start(username: String, password: String, pubsub: pubsub.UpdaterPubSub) {
let spec =
actor.Spec(
init: fn() {
@ -95,6 +92,7 @@ pub fn start(username: String, password: String) {
Pending,
option.None,
self_subject,
pubsub,
),
selector: selector,
)
@ -118,15 +116,26 @@ fn handle_message(message: Message, state: State) -> actor.Next(Message, State)
)
Nil
}
Update -> Nil
_ -> Nil
}
case message {
Update | PeriodicUpdate -> {
use new_state <- result.try(update(credentials, inner_state))
let _ = case new_state.dataset {
option.Some(dataset) ->
glubsub.broadcast(state.pubsub.out, pubsub.NewData(dataset))
option.None -> Ok(Nil)
}
Ok(actor.continue(State(..state, inner: Initialised(new_state))))
}
GetData(reply_subject) -> {
process.send(reply_subject, GetDataResponse(inner_state.dataset))
Ok(actor.continue(state))
}
}
})

View file

@ -0,0 +1,16 @@
import aurinko/updater/types
import gleam/result
import glubsub
pub type OutMessage {
NewData(dataset: types.Dataset)
}
pub type UpdaterPubSub {
UpdaterPubSub(out: glubsub.Topic(OutMessage))
}
pub fn init() {
use out <- result.try(glubsub.new_topic())
Ok(UpdaterPubSub(out))
}

View file

@ -0,0 +1,23 @@
import ap_systems/api
import ap_systems/module_power
import birl
import gleam/dict
import gleamy/red_black_tree_set.{type Set}
pub type ProductionInfo =
api.ProductionInfo
pub type MomentaryPower =
module_power.MomentaryPower
pub type ModulePower {
ModulePower(id: String, power: dict.Dict(birl.Time, MomentaryPower))
}
pub type ModuleDataset {
ModuleDataset(times: Set(birl.Time), datas: dict.Dict(String, ModulePower))
}
pub type Dataset {
Dataset(production_info: ProductionInfo, modules: ModuleDataset)
}

View file

@ -1,8 +1,13 @@
import aurinko/updater/pubsub
import aurinko/web/router
import mist
import wisp
pub fn init(port: Int, secret_key_base: String) {
pub fn init(
port: Int,
secret_key_base: String,
_updater_pubsub: pubsub.UpdaterPubSub,
) {
wisp.configure_logger()
let assert Ok(_) =

View file

@ -0,0 +1,51 @@
import ap_systems/api
import ap_systems/module_power
import aurinko/updater
import aurinko/updater/pubsub
import aurinko/web
import dot_env
import dot_env/env
import gleam/erlang/process
import gleam/int
import gleam/io
import gleam/result
import gleam/string
import glubsub
pub fn main() {
dot_env.new_with_path("./.env")
|> dot_env.set_ignore_missing_file(True)
|> dot_env.load()
let assert Ok(username) = env.get("USERNAME")
let assert Ok(password) = env.get("PASSWORD")
let assert Ok(port) = env.get("PORT")
let assert Ok(port) = int.parse(port)
let assert Ok(secret_key_base) = env.get("SECRET_KEY_BASE")
let assert Ok(updater_pubsub) = pubsub.init()
let assert Ok(subject) = updater.start(username, password, updater_pubsub)
let assert Ok(_) = web.init(port, secret_key_base, updater_pubsub)
io.debug(subject)
process.sleep(15_000)
io.debug(updater.get_data(subject))
process.sleep_forever()
// use cookies <- result.try(
// api.login(username, password) |> result.map_error(string.inspect),
// )
// io.debug(cookies)
// use prod <- result.try(
// api.get_production(cookies) |> result.map_error(string.inspect),
// )
// io.debug(prod)
// use ids <- result.try(
// io.debug(api.get_module_ids(cookies)) |> result.map_error(string.inspect),
// )
// use module_data <- result.try(
// api.get_module_power(cookies, ids) |> result.map_error(string.inspect),
// )
// io.debug(module_power.decode_api_data(module_data))
// |> result.map_error(string.inspect)
}