Compare commits
15 Commits
db-gateway
...
nodes-v1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78167ceb38
|
||
|
|
3cdfbef680
|
||
|
|
94e0518fa6
|
||
|
|
f4ac6e91b0
|
||
|
|
6fd9959329
|
||
|
|
35fbe7c488
|
||
|
|
158767deec
|
||
|
|
bce2417ded
|
||
|
|
53ccda306b
|
||
|
|
71ed4c4bf9
|
||
|
|
e1d274470b
|
||
|
|
f0bc4ee373 | ||
|
|
f71db42f47 | ||
|
|
8b9e215cc6 | ||
|
|
caee92c595 |
@@ -15,6 +15,7 @@ class "Window_status" as win{}
|
||||
class "Hygrometer" as hygro{}
|
||||
class "Thermometer" as thermo{}
|
||||
class "CO2_level" as co2{}
|
||||
class "Battery_level" as batt{}
|
||||
|
||||
sup o-d- ble
|
||||
sup o-u- sens
|
||||
@@ -22,5 +23,6 @@ sens <|-l- win
|
||||
sens <|-u- hygro
|
||||
sens <|-u- thermo
|
||||
sens <|-r- co2
|
||||
sens <|-- batt
|
||||
|
||||
@enduml
|
||||
|
||||
7
nodes/CMakeLists.txt
Normal file
7
nodes/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(plein_de_eeeeeeeeeeee)
|
||||
|
||||
target_sources(app PRIVATE src/main.c src/window_status.c src/window_status.h src/thermometer.c src/thermometer.h src/hygrometer.c src/hygrometer.h src/co2_level.c src/co2_level.h src/supervisor.c src/supervisor.h src/ble_advertiser.c src/ble_advertiser.h)
|
||||
97
nodes/README.rst
Normal file
97
nodes/README.rst
Normal file
@@ -0,0 +1,97 @@
|
||||
.. zephyr:code-sample:: blinky
|
||||
:name: Blinky
|
||||
:relevant-api: gpio_interface
|
||||
|
||||
Blink an LED forever using the GPIO API.
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
The Blinky sample blinks an LED forever using the :ref:`GPIO API <gpio_api>`.
|
||||
|
||||
The source code shows how to:
|
||||
|
||||
#. Get a pin specification from the :ref:`devicetree <dt-guide>` as a
|
||||
:c:struct:`gpio_dt_spec`
|
||||
#. Configure the GPIO pin as an output
|
||||
#. Toggle the pin forever
|
||||
|
||||
See :zephyr:code-sample:`pwm-blinky` for a similar sample that uses the PWM API instead.
|
||||
|
||||
.. _blinky-sample-requirements:
|
||||
|
||||
Requirements
|
||||
************
|
||||
|
||||
Your board must:
|
||||
|
||||
#. Have an LED connected via a GPIO pin (these are called "User LEDs" on many of
|
||||
Zephyr's :ref:`boards`).
|
||||
#. Have the LED configured using the ``led0`` devicetree alias.
|
||||
|
||||
Building and Running
|
||||
********************
|
||||
|
||||
Build and flash Blinky as follows, changing ``reel_board`` for your board:
|
||||
|
||||
.. zephyr-app-commands::
|
||||
:zephyr-app: samples/basic/blinky
|
||||
:board: reel_board
|
||||
:goals: build flash
|
||||
:compact:
|
||||
|
||||
After flashing, the LED starts to blink and messages with the current LED state
|
||||
are printed on the console. If a runtime error occurs, the sample exits without
|
||||
printing to the console.
|
||||
|
||||
Build errors
|
||||
************
|
||||
|
||||
You will see a build error at the source code line defining the ``struct
|
||||
gpio_dt_spec led`` variable if you try to build Blinky for an unsupported
|
||||
board.
|
||||
|
||||
On GCC-based toolchains, the error looks like this:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
error: '__device_dts_ord_DT_N_ALIAS_led_P_gpios_IDX_0_PH_ORD' undeclared here (not in a function)
|
||||
|
||||
Adding board support
|
||||
********************
|
||||
|
||||
To add support for your board, add something like this to your devicetree:
|
||||
|
||||
.. code-block:: DTS
|
||||
|
||||
/ {
|
||||
aliases {
|
||||
led0 = &myled0;
|
||||
};
|
||||
|
||||
leds {
|
||||
compatible = "gpio-leds";
|
||||
myled0: led_0 {
|
||||
gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
The above sets your board's ``led0`` alias to use pin 13 on GPIO controller
|
||||
``gpio0``. The pin flags :c:macro:`GPIO_ACTIVE_LOW` mean the LED is on when
|
||||
the pin is set to its low state, and off when the pin is in its high state.
|
||||
|
||||
Tips:
|
||||
|
||||
- See :dtcompatible:`gpio-leds` for more information on defining GPIO-based LEDs
|
||||
in devicetree.
|
||||
|
||||
- If you're not sure what to do, check the devicetrees for supported boards which
|
||||
use the same SoC as your target. See :ref:`get-devicetree-outputs` for details.
|
||||
|
||||
- See :zephyr_file:`include/zephyr/dt-bindings/gpio/gpio.h` for the flags you can use
|
||||
in devicetree.
|
||||
|
||||
- If the LED is built in to your board hardware, the alias should be defined in
|
||||
your :ref:`BOARD.dts file <devicetree-in-out-files>`. Otherwise, you can
|
||||
define one in a :ref:`devicetree overlay <set-devicetree-overlays>`.
|
||||
11
nodes/app.overlay
Normal file
11
nodes/app.overlay
Normal file
@@ -0,0 +1,11 @@
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/ {
|
||||
buttons{
|
||||
compatible = "gpio-keys";
|
||||
window_switch: window_switch {
|
||||
gpios = <&sx1509b 0 GPIO_ACTIVE_LOW>;
|
||||
label = "Window Switch";
|
||||
};
|
||||
};
|
||||
};
|
||||
3
nodes/prj.conf
Normal file
3
nodes/prj.conf
Normal file
@@ -0,0 +1,3 @@
|
||||
CONFIG_I2C=y
|
||||
CONFIG_SENSOR=y
|
||||
CONFIG_BT=y
|
||||
4281
nodes/release/nodes_v1.0.hex
Normal file
4281
nodes/release/nodes_v1.0.hex
Normal file
File diff suppressed because it is too large
Load Diff
12
nodes/sample.yaml
Normal file
12
nodes/sample.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
sample:
|
||||
name: Blinky Sample
|
||||
tests:
|
||||
sample.basic.blinky:
|
||||
tags:
|
||||
- LED
|
||||
- gpio
|
||||
filter: dt_enabled_alias_with_parent_compat("led0", "gpio-leds")
|
||||
depends_on: gpio
|
||||
harness: led
|
||||
integration_platforms:
|
||||
- frdm_k64f
|
||||
67
nodes/src/ble_advertiser.c
Normal file
67
nodes/src/ble_advertiser.c
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "ble_advertiser.h"
|
||||
|
||||
static const int BT_MS_ADV_UP = 500; // [ms] time during which the advertising is active
|
||||
|
||||
static const char BT_KEY_WINDOW = 0x01;
|
||||
static const char BT_KEY_HUMIDITY = 0x02;
|
||||
static const char BT_KEY_TEMP = 0x03;
|
||||
static const char BT_KEY_CO2_LVL = 0x04;
|
||||
|
||||
// value size [B]
|
||||
static const char BT_PREAMBLE_SIZE = 2;
|
||||
static const char BT_VALUE_SIZE_WINDOW = 1;
|
||||
static const char BT_VALUE_SIZE_HUMIDITY = 1;
|
||||
static const char BT_VALUE_SIZE_TEMP = 2;
|
||||
static const char BT_VALUE_SIZE_CO2_LVL = 4;
|
||||
|
||||
static const int BT_AD_DATA_INDEX_WINDOW = BT_PREAMBLE_SIZE + 1;
|
||||
static const int BT_AD_DATA_INDEX_HUMIDITY = BT_AD_DATA_INDEX_WINDOW + 2;
|
||||
static const int BT_AD_DATA_INDEX_TEMP = BT_AD_DATA_INDEX_HUMIDITY + 2;
|
||||
static const int BT_AD_DATA_INDEX_CO2_LVL = BT_AD_DATA_INDEX_TEMP + 3;
|
||||
|
||||
// sum of all value size + size for all keys
|
||||
static const char BT_AD_TOTAL_SIZE =
|
||||
BT_VALUE_SIZE_WINDOW + BT_VALUE_SIZE_HUMIDITY + BT_VALUE_SIZE_TEMP + BT_VALUE_SIZE_CO2_LVL + 4 + 2;
|
||||
|
||||
static uint8_t ad_data[] = {
|
||||
0xff, 0xff,
|
||||
BT_KEY_WINDOW, 0x00,
|
||||
BT_KEY_HUMIDITY, 0x00,
|
||||
BT_KEY_TEMP, 0x00, 0x00,
|
||||
BT_KEY_CO2_LVL, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
static const struct bt_data ad[] = {
|
||||
BT_DATA(BT_DATA_MANUFACTURER_DATA, ad_data, BT_AD_TOTAL_SIZE),
|
||||
};
|
||||
|
||||
enum error_code ble_init(){
|
||||
return ((0 == bt_enable(NULL)) ? success : init_failed);
|
||||
}
|
||||
|
||||
enum error_code ble_advertise(
|
||||
enum window_status window_value,
|
||||
int hygro_value,
|
||||
int thermo_value,
|
||||
int co2_lvl_value
|
||||
){
|
||||
enum error_code ret = write_failed;
|
||||
int shift = 0;
|
||||
// set values here
|
||||
ad_data[BT_AD_DATA_INDEX_WINDOW] = (uint8_t)(window_value == open ? 0x1 : 0x0);
|
||||
ad_data[BT_AD_DATA_INDEX_HUMIDITY] = (uint8_t)(hygro_value & 0xff);
|
||||
for(int i=0;i<BT_VALUE_SIZE_TEMP;i++){
|
||||
shift = 8 * (BT_VALUE_SIZE_TEMP - i - 1);
|
||||
ad_data[BT_AD_DATA_INDEX_TEMP + i] = (uint8_t)((thermo_value>>(shift)) & 0xff);
|
||||
}
|
||||
for(int i=0;i<BT_VALUE_SIZE_CO2_LVL;i++){
|
||||
shift = 8 * (BT_VALUE_SIZE_CO2_LVL - i - 1);
|
||||
ad_data[BT_AD_DATA_INDEX_CO2_LVL + i] = (uint8_t)((co2_lvl_value>>(shift)) & 0xff);
|
||||
}
|
||||
if(0 == bt_le_adv_start(BT_LE_ADV_NCONN, ad, ARRAY_SIZE(ad), NULL, 0)){
|
||||
k_msleep(BT_MS_ADV_UP);
|
||||
if(0 == bt_le_adv_stop()){
|
||||
ret = success;
|
||||
}else{}
|
||||
}else{}
|
||||
return ret;
|
||||
}
|
||||
19
nodes/src/ble_advertiser.h
Normal file
19
nodes/src/ble_advertiser.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef BLE_ADVERTISER_H
|
||||
#define BLE_ADVERTISER_H
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/bluetooth/assigned_numbers.h>
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
|
||||
#include "error_code.h"
|
||||
#include "window_status.h"
|
||||
|
||||
enum error_code ble_init();
|
||||
enum error_code ble_advertise(
|
||||
enum window_status window_value,
|
||||
int hygro_value,
|
||||
int thermo_value,
|
||||
int co2_lvl_value
|
||||
);
|
||||
|
||||
#endif //BLE_ADVERTISER_H
|
||||
36
nodes/src/co2_level.c
Normal file
36
nodes/src/co2_level.c
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "co2_level.h"
|
||||
|
||||
static const struct device* dev = DEVICE_DT_GET_ONE(ams_ccs811);
|
||||
|
||||
const int CO2_LEVEL_EMPTY_ROOM = 400; // [ppm]
|
||||
|
||||
enum error_code co2_lvl_init(){
|
||||
enum error_code ret = init_failed;
|
||||
if(device_is_ready(dev)){
|
||||
ret = success;
|
||||
}else{}
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum error_code co2_lvl_get_value(int* holder){
|
||||
struct sensor_value temp, humidity, co2;
|
||||
enum error_code ret = read_failed;
|
||||
int temp_value, humidity_value;
|
||||
if( (success == thermo_get_value(&temp_value)) && (success == hygro_get_value(&humidity_value)) ){
|
||||
// temperature conversion from deci, no function in the API
|
||||
temp.val1 = temp_value/10;
|
||||
temp.val2 = temp_value%10;
|
||||
// humidity conversion is straight away
|
||||
humidity.val1 = humidity_value;
|
||||
|
||||
if(
|
||||
(0 == ccs811_envdata_update(dev, &temp, &humidity)) &&
|
||||
(0 == sensor_sample_fetch(dev)) &&
|
||||
(0 == sensor_channel_get(dev, SENSOR_CHAN_CO2, &co2))
|
||||
){
|
||||
*holder = co2.val1; // taking only the integer part
|
||||
ret = success;
|
||||
}else{}
|
||||
}else{}
|
||||
return success;
|
||||
}
|
||||
16
nodes/src/co2_level.h
Normal file
16
nodes/src/co2_level.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef CO2_LEVEL_H
|
||||
#define CO2_LEVEL_H
|
||||
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include <zephyr/drivers/sensor/ccs811.h>
|
||||
|
||||
#include "error_code.h"
|
||||
#include "hygrometer.h"
|
||||
#include "thermometer.h"
|
||||
|
||||
extern const int CO2_LEVEL_EMPTY_ROOM; // [ppm]
|
||||
|
||||
enum error_code co2_lvl_init();
|
||||
enum error_code co2_lvl_get_value(int* holder);
|
||||
|
||||
#endif //CO2_LEVEL_H
|
||||
15
nodes/src/error_code.h
Normal file
15
nodes/src/error_code.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef ERROR_CODE_H
|
||||
#define ERROR_CODE_H
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
enum error_code{
|
||||
success = 0,
|
||||
init_failed,
|
||||
read_failed,
|
||||
write_failed,
|
||||
error_unknown,
|
||||
error_code_last, // iteration purpose
|
||||
};
|
||||
|
||||
#endif //ERROR_CODE_H
|
||||
23
nodes/src/hygrometer.c
Normal file
23
nodes/src/hygrometer.c
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "hygrometer.h"
|
||||
|
||||
static const struct device* dev = DEVICE_DT_GET_ONE(st_hts221);
|
||||
|
||||
enum error_code hygro_init(){
|
||||
enum error_code ret = init_failed;
|
||||
if(device_is_ready(dev)){
|
||||
ret = success;
|
||||
}else{}
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum error_code hygro_get_value(int* holder){
|
||||
enum error_code ret = read_failed;
|
||||
struct sensor_value humidity;
|
||||
if( (sensor_sample_fetch(dev) >= 0) &&
|
||||
(sensor_channel_get(dev, SENSOR_CHAN_HUMIDITY, &humidity) >= 0)
|
||||
){
|
||||
*holder = humidity.val1; //taking only the integer part
|
||||
ret = success;
|
||||
}else{}
|
||||
return ret;
|
||||
}
|
||||
13
nodes/src/hygrometer.h
Normal file
13
nodes/src/hygrometer.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef HYGROMETER_H
|
||||
#define HYGROMETER_H
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
|
||||
#include "error_code.h"
|
||||
|
||||
enum error_code hygro_init();
|
||||
enum error_code hygro_get_value(int* holder);
|
||||
|
||||
#endif //HYGROMETER_H
|
||||
18
nodes/src/main.c
Normal file
18
nodes/src/main.c
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
|
||||
#include "supervisor.h"
|
||||
|
||||
int main(void){
|
||||
supervisor_init();
|
||||
supervisor_run();
|
||||
// should never come here
|
||||
return 0;
|
||||
}
|
||||
53
nodes/src/supervisor.c
Normal file
53
nodes/src/supervisor.c
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "supervisor.h"
|
||||
|
||||
const int SLEEP_GRANULARITY = 2; // [min]
|
||||
const int SLEEP_MIN_DURATION = SLEEP_GRANULARITY; // [min]
|
||||
const int SLEEP_MAX_DURATION = 30; // [min]
|
||||
|
||||
enum error_code supervisor_init(){
|
||||
enum error_code ret = init_failed;
|
||||
ret = ble_init();
|
||||
if(success == ret){
|
||||
ret = co2_lvl_init();
|
||||
if(success == ret){
|
||||
ret = hygro_init();
|
||||
if(success == ret){
|
||||
ret = thermo_init();
|
||||
if(success == ret){
|
||||
ret = window_init();
|
||||
}else{}
|
||||
}else{}
|
||||
}else{}
|
||||
}else{}
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum error_code supervisor_run(){
|
||||
int co2_lvl_value = -1;
|
||||
int hygro_value = -1;
|
||||
int thermo_value = -1;
|
||||
enum window_status window_value = unknown;
|
||||
enum error_code co2_lvl_status, hygro_status, thermo_status, window_status;
|
||||
int current_sleep_time = SLEEP_MIN_DURATION;
|
||||
while(1){
|
||||
co2_lvl_status = co2_lvl_get_value(&co2_lvl_value);
|
||||
hygro_status = hygro_get_value(&hygro_value);
|
||||
thermo_status = thermo_get_value(&thermo_value);
|
||||
window_status = window_get_value(&window_value);
|
||||
// maybe change arguments order
|
||||
// todo : manage special values
|
||||
ble_advertise(window_value, hygro_value, thermo_value, co2_lvl_value);
|
||||
if((co2_lvl_value > CO2_LEVEL_EMPTY_ROOM) || (window_value == open)){
|
||||
// there are people in the room, or someone forgot to close the window
|
||||
current_sleep_time = SLEEP_MIN_DURATION;
|
||||
}else{
|
||||
// no one is in the room, we can wait a liitle bit longer before getting the next data point
|
||||
current_sleep_time += SLEEP_GRANULARITY;
|
||||
if(current_sleep_time > SLEEP_MAX_DURATION){
|
||||
current_sleep_time = SLEEP_MAX_DURATION;
|
||||
}else{}
|
||||
}
|
||||
k_sleep(K_MINUTES(current_sleep_time));
|
||||
}
|
||||
return error_unknown; // should never return
|
||||
}
|
||||
21
nodes/src/supervisor.h
Normal file
21
nodes/src/supervisor.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef SUPERVISOR_H
|
||||
#define SUPERVISOR_H
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include "error_code.h"
|
||||
|
||||
#include "ble_advertiser.h"
|
||||
#include "window_status.h"
|
||||
#include "thermometer.h"
|
||||
#include "hygrometer.h"
|
||||
#include "co2_level.h"
|
||||
|
||||
extern const int SLEEP_GRANULARITY; // [min]
|
||||
extern const int SLEEP_MIN_DURATION; // [min]
|
||||
extern const int SLEEP_MAX_DURATION; // [min]
|
||||
|
||||
enum error_code supervisor_init();
|
||||
enum error_code supervisor_run();
|
||||
|
||||
#endif //SUPERVISOR_H
|
||||
23
nodes/src/thermometer.c
Normal file
23
nodes/src/thermometer.c
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "thermometer.h"
|
||||
|
||||
static const struct device* dev = DEVICE_DT_GET_ONE(st_hts221);
|
||||
|
||||
enum error_code thermo_init(){
|
||||
enum error_code ret = init_failed;
|
||||
if(device_is_ready(dev)){
|
||||
ret = success;
|
||||
}else{}
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum error_code thermo_get_value(int* holder){
|
||||
enum error_code ret = read_failed;
|
||||
struct sensor_value temp;
|
||||
if( (sensor_sample_fetch(dev) >= 0) &&
|
||||
(sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp) >= 0)
|
||||
){
|
||||
*holder = sensor_value_to_deci(&temp);
|
||||
ret = success;
|
||||
}else{}
|
||||
return ret;
|
||||
}
|
||||
13
nodes/src/thermometer.h
Normal file
13
nodes/src/thermometer.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef THERMOMETER_H
|
||||
#define THERMOMETER_H
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
|
||||
#include "error_code.h"
|
||||
|
||||
enum error_code thermo_init();
|
||||
enum error_code thermo_get_value(int* holder);
|
||||
|
||||
#endif //THERMOMETER_H
|
||||
17
nodes/src/window_status.c
Normal file
17
nodes/src/window_status.c
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "window_status.h"
|
||||
|
||||
static const struct gpio_dt_spec window_switch = GPIO_DT_SPEC_GET(DT_NODELABEL(window_switch), gpios);
|
||||
|
||||
enum error_code window_init(){
|
||||
enum error_code ret = init_failed;
|
||||
if(gpio_is_ready_dt(&window_switch) && gpio_pin_configure_dt(&window_switch, GPIO_INPUT)){
|
||||
ret = success;
|
||||
}else{}
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum error_code window_get_value(enum window_status* holder){
|
||||
// active high
|
||||
*holder = (gpio_pin_get_dt(&window_switch) ? closed : open);
|
||||
return success;
|
||||
}
|
||||
18
nodes/src/window_status.h
Normal file
18
nodes/src/window_status.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef WINDOW_STATUS_H
|
||||
#define WINDOW_STATUS_H
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
|
||||
#include "error_code.h"
|
||||
|
||||
enum window_status{
|
||||
closed = 0,
|
||||
open,
|
||||
unknown,
|
||||
windows_status_last, // iteration purpose
|
||||
};
|
||||
|
||||
enum error_code window_init();
|
||||
enum error_code window_get_value(enum window_status* holder);
|
||||
|
||||
#endif //WINDOW_STATUS_
|
||||
Reference in New Issue
Block a user