-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Along with the IOT2050 SM variant, some IOT2050 device specific events are recognized as being important for field applications, including: - Device power up - Device power loss - Device reboot - Device case open (uncover) - Device tilting - Device watchdog reset - External module events Although it's possible to collect all these events from variant source, such as syslog, or by customized coding to get the sensor event, it's valuable to provide an unified method or portal to collect all these predefined events from one place. Then this service is added to serve the above purpose, it reads the power up, power loss, eio, tilt, uncover, and (possibly) the watchdog reset events, then writes them into syslog and makes them be readable from gRPC interface. This service could be used directly, or as a base/reference for customization. Signed-off-by: Li Hua Qian <[email protected]>
- Loading branch information
1 parent
2da7bd1
commit c374ec0
Showing
16 changed files
with
878 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
# IOT2050 Event Record | ||
|
||
IOT2050 Event Record is using for reading and recording events, such as | ||
power up, power loss, tilted, uncovered, watchdog reset and eio events. | ||
|
||
The core is a RPC service implemented with the help of gPRC. | ||
|
||
## Event record services | ||
|
||
The `iot2050-event-record.service` and `iot2050-event-serve.service` are systemd | ||
services, they could be managed by `systemctl`. The `iot2050-event-record` collects events from various source then consume the API exposed by `iot2050-event-serve` | ||
to wrap the collected events as `IOT2050-EventRecord` events and save them to | ||
syslog. Then these wrapped events could be read by `journalctl` or by gRPC APIs. | ||
|
||
## Predefined events | ||
|
||
### Power events and Extended IO(EIO) events | ||
|
||
Power events and EIO events are injected to `journal(syslog)` by default. | ||
|
||
To check them on IOT2050: | ||
|
||
```sh | ||
root@iot2050-debian:~# journalctl SYSLOG_IDENTIFIER=IOT2050-EventRecord | ||
Oct 23 22:36:12 iot2050-debian IOT2050-EventRecord[323]: IOT2050_EVENTS.power: 2023-10-23 22:36:00 the device is powered up | ||
Oct 23 22:40:21 iot2050-debian IOT2050-EventRecord[323]: IOT2050_EVENTS.power: 2023-10-23 22:34:4 [2] power loss | ||
Oct 23 22:40:21 iot2050-debian IOT2050-EventRecord[323]: IOT2050_EVENTS.eio: 2023-10-23 22:12:54 [11] slot1 lost | ||
``` | ||
|
||
### Sensor events | ||
|
||
By default, the sensor events, i.e. tilted and uncovered events, are disabled. | ||
To enable them, please create a systemd drop-in for `iot2050-event-record.service`, | ||
as follows: | ||
|
||
```sh | ||
cp /usr/lib/iot2050/event/iot2050-event-record.conf /etc/systemd/system/iot2050-event-record.service.d/ | ||
``` | ||
|
||
### Watchdog events | ||
|
||
If watchdog event recording is expected, please refer to [WATCHDOG.md](./WATCHDOG.md). | ||
|
||
## Development | ||
|
||
### How to inject a new event? | ||
|
||
First, please link the `EventInterface` to the customized application. | ||
```sh | ||
# In IOT DUT | ||
ln -s /usr/lib/iot2050/event/gRPC/EventInterface /path/to/customized-app/gRPC/EventInterface | ||
|
||
# In Source code | ||
ln -s recipes-app/iot2050-event-record/files/gRPC/EventInterface /path/to/customized-app/gRPC/EventInterface | ||
``` | ||
|
||
Then, use the `Write` and `Read` functions to communicate with | ||
`iot2050-event-serve.service`, as follows: | ||
|
||
```python | ||
import grpc | ||
from gRPC.EventInterface.iot2050_event_pb2 import ( | ||
WriteRequest, | ||
ReadRequest | ||
) | ||
from gRPC.EventInterface.iot2050_event_pb2_grpc import EventRecordStub | ||
|
||
|
||
def write_event(event_type, event): | ||
with grpc.insecure_channel(iot2050_event_api_server) as channel: | ||
stub = EventRecordStub(channel) | ||
response = stub.Write(WriteRequest(event_type=event_type, event=event)) | ||
|
||
if response.status: | ||
print(f'Event Record writes event result: {response.status}') | ||
print(f'Event Record writes event message: {response.message}') | ||
|
||
def read_event(event_type): | ||
with grpc.insecure_channel(iot2050_event_api_server) as channel: | ||
stub = EventRecordStub(channel) | ||
response = stub.Read(ReadRequest(event_type=event_type)) | ||
return response.event | ||
``` | ||
|
||
And, please find the api definition in `gRPC/EventInterface/iot2050-event.proto`. | ||
|
||
### Regenerate the gRPC python modules if proto file changed | ||
|
||
If the `proto` file needs to be changed when customizing a new application | ||
to inject event, please update the gRPC in the original path as follows. | ||
|
||
```sh | ||
python3 -m grpc_tools.protoc -I. --python_out=. --pyi_out=. --grpc_python_out=. gRPC/EventInterface/iot2050-event.proto | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
Due to the fact that the watchdog is non-stoppable, getting a watchdog | ||
event always requires opening the watchdog and feeding the watchdog. So | ||
the watchdog event could not be included into the `iot2050-event-record` | ||
service. | ||
|
||
This README file explains how to get the watchdog reset status and how to | ||
inject it into `iot2050-event-record` service. | ||
|
||
# How to get the watchdog reset status? | ||
|
||
The `wdt_example.py` below shows how to get the watchdog reset status. | ||
|
||
```py | ||
import array | ||
import fcntl | ||
import os | ||
import psutil | ||
import time | ||
from datetime import datetime | ||
|
||
# Implement _IOR function for wdt kernel ioctl function | ||
_IOC_NRBITS = 8 | ||
_IOC_TYPEBITS = 8 | ||
_IOC_SIZEBITS = 14 | ||
_IOC_DIRBITS = 2 | ||
|
||
_IOC_NRSHIFT = 0 | ||
_IOC_TYPESHIFT =(_IOC_NRSHIFT+_IOC_NRBITS) | ||
_IOC_SIZESHIFT =(_IOC_TYPESHIFT+_IOC_TYPEBITS) | ||
_IOC_DIRSHIFT =(_IOC_SIZESHIFT+_IOC_SIZEBITS) | ||
|
||
_IOC_NONE = 0 | ||
_IOC_WRITE = 1 | ||
_IOC_READ = 2 | ||
def _IOC(direction,type,nr,size): | ||
return (((direction) << _IOC_DIRSHIFT) | | ||
((type) << _IOC_TYPESHIFT) | | ||
((nr) << _IOC_NRSHIFT) | | ||
((size) << _IOC_SIZESHIFT)) | ||
def _IOR(type, number, size): | ||
return _IOC(_IOC_READ, type, number, size) | ||
|
||
WDIOC_GETBOOTSTATUS = _IOR(ord('W'), 2, 4) | ||
WDIOF_CARDRESET = 0x20 | ||
WDT_PATH = "/dev/watchdog" | ||
|
||
EVENT_STRINGS = { | ||
"wdt": "{} watchdog reset is detected", | ||
"no-wdt": "{} watchdog reset isn't detected" | ||
} | ||
|
||
def feeding_the_watchdog(fd): | ||
while True: | ||
ret = os.write(fd, b'watchdog') | ||
print("Feeding the watchdog ...") | ||
# Let's say the watchdog timeout is more than 30 s | ||
time.sleep(30) | ||
|
||
def record_wdt_events(): | ||
status = array.array('h', [0]) | ||
fd = os.open(WDT_PATH, os.O_RDWR) | ||
if fcntl.ioctl(fd, WDIOC_GETBOOTSTATUS, status, 1) < 0: | ||
print("Failed to get wdt boot status!") | ||
|
||
boot_time = datetime.fromtimestamp(psutil.boot_time()) | ||
if (WDIOF_CARDRESET & status[0]): | ||
print(EVENT_STRINGS["wdt"].format(boot_time)) | ||
else: | ||
print(EVENT_STRINGS["no-wdt"].format(boot_time)) | ||
|
||
feeding_the_watchdog(fd) | ||
|
||
os.close(fd) | ||
|
||
if __name__ == "__main__": | ||
record_wdt_events() | ||
``` | ||
|
||
# How to inject it into iot2050-event-record? | ||
|
||
Please refer to [README.md](./README.md). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../../iot2050-eio-manager/files/gRPC/EIOManager |
61 changes: 61 additions & 0 deletions
61
recipes-app/iot2050-event-record/files/gRPC/EventInterface/iot2050-event.proto
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* | ||
* Copyright (c) Siemens AG, 2023 | ||
* | ||
* Authors: | ||
* Li Hua Qian <[email protected]> | ||
* | ||
* SPDX-License-Identifier: MIT | ||
*/ | ||
|
||
syntax = "proto3"; | ||
|
||
package eventrecord; | ||
|
||
service EventRecord { | ||
rpc Write (WriteRequest) returns (WriteReply) {} | ||
rpc Read (ReadRequest) returns (ReadReply) {} | ||
} | ||
|
||
/* ----------------- Write event ----------------- */ | ||
/* WriteRequest | ||
* - event_type: a string to present event type | ||
* - "IOT2050_EVENT.xxx" means IOT2050 standard events | ||
* - "" or other strings mean customized events | ||
* - event: the event content to write | ||
*/ | ||
message WriteRequest { | ||
string event_type = 1; | ||
string event = 2; | ||
} | ||
|
||
/* WriteReply | ||
* - status: 0 means successful | ||
* others mean error | ||
* - message: the detail write message | ||
*/ | ||
message WriteReply { | ||
int32 status = 1; | ||
string message = 2; | ||
} | ||
|
||
/* ----------------- Read event ----------------- */ | ||
/* ReadRequest | ||
* - event_type: a string to present event type | ||
* - "IOT2050_EVENT.xxx" means IOT2050 standard events | ||
* - "" means to all types of events | ||
*/ | ||
message ReadRequest { | ||
string event_type = 1; | ||
} | ||
|
||
/* ReadReply | ||
* - status: 0 means successful | ||
* others mean error | ||
* - message: the detail write message | ||
* - event: the read back event content | ||
*/ | ||
message ReadReply { | ||
int32 status = 1; | ||
string message = 2; | ||
string event = 3; | ||
} |
33 changes: 33 additions & 0 deletions
33
recipes-app/iot2050-event-record/files/gRPC/EventInterface/iot2050_event_pb2.py
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
37 changes: 37 additions & 0 deletions
37
recipes-app/iot2050-event-record/files/gRPC/EventInterface/iot2050_event_pb2.pyi
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
from google.protobuf import descriptor as _descriptor | ||
from google.protobuf import message as _message | ||
from typing import ClassVar as _ClassVar, Optional as _Optional | ||
|
||
DESCRIPTOR: _descriptor.FileDescriptor | ||
|
||
class WriteRequest(_message.Message): | ||
__slots__ = ["event_type", "event"] | ||
EVENT_TYPE_FIELD_NUMBER: _ClassVar[int] | ||
EVENT_FIELD_NUMBER: _ClassVar[int] | ||
event_type: str | ||
event: str | ||
def __init__(self, event_type: _Optional[str] = ..., event: _Optional[str] = ...) -> None: ... | ||
|
||
class WriteReply(_message.Message): | ||
__slots__ = ["status", "message"] | ||
STATUS_FIELD_NUMBER: _ClassVar[int] | ||
MESSAGE_FIELD_NUMBER: _ClassVar[int] | ||
status: int | ||
message: str | ||
def __init__(self, status: _Optional[int] = ..., message: _Optional[str] = ...) -> None: ... | ||
|
||
class ReadRequest(_message.Message): | ||
__slots__ = ["event_type"] | ||
EVENT_TYPE_FIELD_NUMBER: _ClassVar[int] | ||
event_type: str | ||
def __init__(self, event_type: _Optional[str] = ...) -> None: ... | ||
|
||
class ReadReply(_message.Message): | ||
__slots__ = ["status", "message", "event"] | ||
STATUS_FIELD_NUMBER: _ClassVar[int] | ||
MESSAGE_FIELD_NUMBER: _ClassVar[int] | ||
EVENT_FIELD_NUMBER: _ClassVar[int] | ||
status: int | ||
message: str | ||
event: str | ||
def __init__(self, status: _Optional[int] = ..., message: _Optional[str] = ..., event: _Optional[str] = ...) -> None: ... |
Oops, something went wrong.