- Научитесь подключаться к серверу и выполнять HTTP POST запросы на сервер, передавая ему уже готовые закодированные сообщения.
- Научитесь кодировать и декодировать URL-encoded unpadded base64 для отправки на сервер и приёма данных от сервера.
- Научитесь кодировать и декодировать пакеты из бинарной формы в ваше внутреннее представление.
- Научитесь выявлять структуру сети устройств с помощью WHOISHERE. Научитесь управлять одной лампой и одним выключателем. Реализуйте весь требуемый функционал.
Напишите программу, которая реализует функции хаба умного дома. В умном доме размещены сенсоры, то есть устройства, измеряющие параметры окружающей среды, актуаторы, то есть устройства, воздействующие на среду в доме, таймер,
передающий показания текущего времени, и хаб, то есть устройство, которое управляет всеми остальными устройствами в умном доме.
Все устройства находятся в общей коммуникационной среде сети
. Информация по сети передаётся с помощью пакетов.
Пакет может отправляться одному устройству, либо всем устройствам одновременно broadcast
.
Каждое устройство, включая хаб, имеет свой уникальный 14-битный
адрес в сети. Коммуникационная среда ненадёжна, то есть пакеты могут в сети теряться либо портиться, но пакеты не дублируются, то есть ситуация,
когда получатель получает один и тот же пакет несколько раз, невозможна. Для моделирования функциональности хаба умного дома ваша программа должна получать данные из сети, и в качестве реакции на полученные данные отправлять данные в сеть.
В рамках данной задачи получение данных из сети и отправка данных в сеть моделируется с помощью HTTP POST
запроса на специальный сервер, который моделирует функционирование остальных устройств умного дома.
-
byte
— беззнаковое 8-битное значение (октет). -
bytes
— массив байтов переменного размера, этот тип должен конкретизироваться в описании конкретных форматов пакетов в зависимости от типа пакета и типа устройства. -
string
— строка. Первый байт строки хранит ее длину, затем идут символы строки, в строке допустимы символы (байты) с кодами 32-126 (включительно). -
varuint
— беззнаковое целое число в формате ULEB128. -
[]T
— массив элементов типа T, первый байт памяти, отводимой под массив, это длина массива, далее следуют байты, кодирующие элементы массива. -
struct
— структура, состоящая из полей произвольного типа. Структура может иметь переменный размер, поскольку поля структуры могут иметь переменный размер, например varuint, string, и т. п.
Каждый пакет, передаваемый по сети, описывается следующим образом:
type packet struct {
length byte
payload bytes
crc8 byte
};
Где:
length
— это размер поля payload в октетах (байтах);payload
— данные, передаваемые в пакете, конкретный формат данных для каждого типа пакета описывается ниже;crc8
— контрольная сумма поля payload, вычисленная по алгоритму cyclic redundancy check 8
Полезная нагрузка (поле payload) имеет следующую структуру:
type payload struct {
src varuint
dst varuint
serial varuint
dev_type byte
cmd byte
cmd_body bytes
};
Где:
src
- это 14-битный “адрес” устройства-отправителя;dst
- 14-битный “адрес” устройства-получателя, причем адреса 0x0000 и 0x3FFF (16383) зарезервированы. Адрес 0x3FFF означает “широковещательную” рассылку, то есть данные адресованы всем устройствам одновременно;serial
- это порядковый номер пакета, отправленного устройством, от момента его включения. serial нумеруется с 1;dev_type
- это тип устройства, к которому относится пакет; cmd - это команда протокола;cmd_body
- формат которого зависит от команды протокола. Тип устройстваdev_type
и командаcmd
в совокупности определяют данные, которые передаются вcmd_body
, как будет описано далее. Обратите внимание, чтоdev_type
- это тип устройства, к которому относится данная команда. Например, если хаб посылает лампе команду на включение SETSTATUS, то иdev_type
у соответствующего пакета должен быть равен 4 (лампа).
0x01 - WHOISHERE
— отправляется устройством, желающим обнаружить своих соседей в сети. Адрес рассылкиdst
должен быть широковещательным0x3FFF
. Поле cmd_body описывает характеристики самого устройства в виде структуры:
type device struct {
dev_name string
dev_props bytes
};
Содержимое dev_props определяется в зависимости от типа устройства.
0x02 - IAMHERE
— отправляется устройством, получившим командуWHOISHERE
, и содержит информацию о самом устройстве в полеcmd_body
. Команда IAMHERE отправляется строго в ответ наWHOISHERE
. Команда отправляется на широковещательный адрес.0x03 - GETSTATUS
— отправляется хабом какому-либо устройству для чтения состояния устройства. Если устройство не поддерживает команду GETSTATUS (например, таймер), команда игнорируется. Поле cmd_body должно быть пустым.0x04 - STATUS
— отправляется устройством хабу и как ответ на запросыGETSTATUS
,SETSTATUS
, и самостоятельно при изменении состояния устройства. Например, переключатель(switch)
отправляет сообщениеSTATUS
в момент переключения. В этом случае адресом получателя устанавливается устройство, которое последнее отправило данному устройству командуGETSTATUS
. Если такой команды ещё не поступало, сообщениеSTATUS
не отправляется никому.0x05 - SETSTATUS
— отправляется хабом какому-либо устройству, чтобы устройство изменило свое состояние, например, чтобы включилась лампа. Если устройство не поддерживает изменение состояния (например, таймер), команда игнорируется.dev_type
должно устанавливаться в тип устройства, для которого предназначено сообщение.0x06 - TICK
— тик таймера, отправляется таймером. Периодичность отправления не гарантируется, но если на некоторый момент времени запланировано событие, то срабатывание события должно наступать, когда время, передаваемое в сообщенииTICK
становится больше или равно запланированному. Полеcmd_body
содержит следующие данные:
type timer_cmd_body struct {
timestamp varuint
};
0x01 - SmartHub
— это устройство, которое моделирует ваша программа, оно единственное устройство этого типа в сети;0x02 - EnvSensor
— датчик характеристик окружающей среды (температура, влажность, освещенность, загрязнение воздуха);0x03 - Switch
— переключатель;0x04 - Lamp — лампа
;0x05 - Socket
— розетка;0x06 - Clock
— часы, которые широковещательно рассылают сообщения TICK. Часы гарантрированно присутствуют в сети и только в одном экземпляре.
Версия 0.2.0 поддерживает два сценария работы.
В этом сценарии в сети находится единственное устройство - таймер с именем TIMER01
. Таймер шлёт сообщения о текущем времени каждые 100мс модельного времени.
В этом сценарии в сети находится таймер TIMER01
, лампа LAMP02
и выключатель SWITCH03
, коммутированный с лампой. Выключатель изначально находится в состоянии "выключен" и периодически меняет свое состояние. Таймер шлет сообщения о текущем времени каждые 100мс модельного времени.
Демо-сервер может использоваться как утилита для кодирования и декодирования пакетов.
Обратите внимание, что JSON-формат используется только для отладочных целей. По сети передаются пакеты в бинарном формате, закодированные в Base64.
Опция -B
выполняет декодирование пакета из base64 в JSON-форму, например, если выполнить из командной строки Unix команду
echo DbMG_38BBgaI0Kv6kzGK | ./smarthome -B
на стандартный поток вывода будет напечатано
[
{
"length": 13,
"payload": {
"src": 819,
"dst": 16383,
"serial": 1,
"dev_type": 6,
"cmd": 6,
"cmd_body": {
"timestamp": 1688984021000
}
},
"crc8": 138
}
]
Опция -J
выполняет преобразование из JSON-представления в base64-представления для отправки. Например, если запустить из командной строки команду
./smarthome -J
и на стандартном потоке ввода ввести
[
{
"length": 13,
"payload": {
"src": 819,
"dst": 16383,
"serial": 1,
"dev_type": 6,
"cmd": 6,
"cmd_body": {
"timestamp": 1688984021000
}
},
"crc8": 138
}
]
на стандартный поток вывода будет напечатано
DbMG_38BBgaI0Kv6kzGK
Опция -K
выполняет преобразование из JSON-представления в бинарное представление. Так можно посмотреть, как представляется пакет в бинарной форме до его кодирования в Base64.
Например, если запустить из командной строки команду
./smarthome -K | hexdump -C
и на стандартном потоке ввода ввести
[
{
"length": 13,
"payload": {
"src": 819,
"dst": 16383,
"serial": 1,
"dev_type": 6,
"cmd": 6,
"cmd_body": {
"timestamp": 1688984021000
}
},
"crc8": 138
}
]
на стандартный поток вывода будет напечатано
00000000 0d b3 06 ff 7f 01 06 06 88 d0 ab fa 93 31 8a |.............1.|
Внимание! Команды даны в предположении, что вы используете Linux или MacOS. В windows соответствующие команды могут быть другими.
Ниже приведены примеры пакетов в base64 для всех возможных пар из типа устройства и команды.
- SmartHub, WHOISHERE (1, 1):
DAH_fwEBAQVIVUIwMeE
- SmartHub, IAMHERE (1, 2):
DAH_fwIBAgVIVUIwMak
- EnvSensor, WHOISHERE (2, 1):
OAL_fwMCAQhTRU5TT1IwMQ8EDGQGT1RIRVIxD7AJBk9USEVSMgCsjQYGT1RIRVIzCAAGT1RIRVI03Q
- EnvSensor, IAMHERE (2, 2):
OAL_fwQCAghTRU5TT1IwMQ8EDGQGT1RIRVIxD7AJBk9USEVSMgCsjQYGT1RIRVIzCAAGT1RIRVI09w
- EnvSensor, GETSTATUS (2, 3):
BQECBQIDew
- EnvSensor, STATUS (2, 4):
EQIBBgIEBKUB4AfUjgaMjfILrw
- Switch, WHOISHERE (3, 1):
IgP_fwcDAQhTV0lUQ0gwMQMFREVWMDEFREVWMDIFREVWMDO1
- Switch, IAMHERE (3, 2):
IgP_fwgDAghTV0lUQ0gwMQMFREVWMDEFREVWMDIFREVWMDMo
- Switch, GETSTATUS (3, 3):
BQEDCQMDoA
- Switch, STATUS (3, 4):
BgMBCgMEAac
- Lamp, WHOISHERE (4, 1):
DQT_fwsEAQZMQU1QMDG8
- Lamp, IAMHERE (4, 2):
DQT_fwwEAgZMQU1QMDGU
- Lamp, GETSTATUS (4, 3):
BQEEDQQDqw
- Lamp, STATUS (4, 4):
BgQBDgQEAaw
- Lamp, SETSTATUS (4, 5):
BgEEDwQFAeE
- Socket, WHOISHERE (5, 1):
DwX_fxAFAQhTT0NLRVQwMQ4
- Socket, IAMHERE (5, 2):
DwX_fxEFAghTT0NLRVQwMc0
- Socket, GETSTATUS (5, 3):
BQEFEgUD5A
- Socket, STATUS (5, 4):
BgUBEwUEAQ8
- Socket, SETSTATUS (5, 5):
BgEFFAUFAQc
- Clock, IAMHERE (6, 2):
Dgb_fxUGAgdDTE9DSzAxsw
- Clock, TICK (6, 6):
DAb_fxgGBpabldu2NNM
-
Сценарий_1
(В этом сценарии в сети находится единственное устройство - таймер с именемTIMER01
. Таймер шлёт сообщения о текущем времени каждые 100мс модельного времени.) -
Сценарий_2
(В этом сценарии в сети находится таймерTIMER01
, лампаLAMP02
и выключательSWITCH03
, коммутированный с лампой. Выключатель изначально находится в состоянии "выключен" и периодически меняет свое состояние. Таймер шлет сообщения о текущем времени каждые 100мс модельного времени.)