Implement scheduling for EG-PMS2
The EG-PMS has a storage format for schedules that is different to prior versions of the hardware. Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
This commit is contained in:
parent
1c9a2a848d
commit
d8587a5d25
|
@ -1,3 +1,4 @@
|
|||
Support scheduling on EG-PMS2
|
||||
01 Apr 2020 - 4.6
|
||||
Add option to specify powerstrip by USB Bus:Device
|
||||
Improve stability of webserver
|
||||
|
|
|
@ -25,7 +25,9 @@ The first 5 bytes are:
|
|||
|
||||
The next groups of five bytes indicate the scheduled actions:
|
||||
|
||||
* 1 byte indicating the action: 0x00 = off, 0x01 = on
|
||||
* 1 byte where
|
||||
bit 0 indicates the action: 0 off, 1 on, and
|
||||
bit 1 indicates if the action shall be repeated in a loop.
|
||||
* 4 bytes seconds since 1970-01-01 in LSB format indicating the scheduled time
|
||||
|
||||
The last 5 bytes are:
|
||||
|
|
|
@ -43,7 +43,8 @@ pkgdata3_DATA = \
|
|||
endif
|
||||
|
||||
libsispmctl_la_SOURCES = \
|
||||
process.c sispm_ctl.c nethelp.c socket.c sispm_ctl.h nethelp.h socket.h
|
||||
process.c sispm_ctl.c nethelp.c schedule.c socket.c \
|
||||
sispm_ctl.h nethelp.h socket.h
|
||||
|
||||
sispmctl_SOURCES = main.c
|
||||
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Conversion routines for schedules
|
||||
*
|
||||
* Copyright (c) 2017 Heinrich Schuchardt
|
||||
*/
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "sispm_ctl.h"
|
||||
|
||||
#define PMS2_BUFFER_SIZE 0x28
|
||||
|
||||
static unsigned char
|
||||
*pms2_write_block(uint8_t action, uint32_t time, unsigned char *ptr)
|
||||
{
|
||||
*ptr++ = action;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
*ptr++ = (uint8_t)time;
|
||||
time >>= 8;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* pms2_read_block() - read a single action for EG-PMS2
|
||||
*
|
||||
* @action: action to be taken
|
||||
* @time: time when to take the action
|
||||
* @ptr: pointer to current action
|
||||
* Return: pointer to next action
|
||||
*/
|
||||
static const unsigned char
|
||||
*pms2_read_block(uint8_t *action, uint32_t *time, const unsigned char *ptr)
|
||||
{
|
||||
*action = *ptr++;
|
||||
*time = 0;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
*time >>= 8;
|
||||
*time += (uint32_t)*ptr++ << 24;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* pms2_schedule_to_buffer() - convert schedule to device buffer
|
||||
*
|
||||
* @schedule: schedule
|
||||
* @buffer: 40 character buffer
|
||||
*
|
||||
* Return: 0 = success
|
||||
*/
|
||||
int pms2_schedule_to_buffer(const struct plannif *schedule,
|
||||
unsigned char *buffer)
|
||||
{
|
||||
unsigned char *ptr = buffer;
|
||||
uint32_t loop_ref, start = (uint32_t)schedule->timeStamp;
|
||||
unsigned char action;
|
||||
int i;
|
||||
|
||||
memset(buffer, 0, PMS2_BUFFER_SIZE);
|
||||
|
||||
ptr = pms2_write_block(3 * schedule->socket + 1, start, ptr);
|
||||
|
||||
for (i = 0; i < 7; ++i) {
|
||||
action = schedule->actions[i + 1].switchOn;
|
||||
start += 60 * schedule->actions[i].timeForNext;
|
||||
if (!i)
|
||||
loop_ref = start;
|
||||
if (action > 1)
|
||||
break;
|
||||
ptr = pms2_write_block(action, start, ptr);
|
||||
}
|
||||
if (action <= 1) {
|
||||
fprintf(stderr, "Schedule has too many items\n");
|
||||
return -1;
|
||||
}
|
||||
if (schedule->actions[i].timeForNext)
|
||||
start -= loop_ref;
|
||||
else
|
||||
start = 0;
|
||||
pms2_write_block(0xe5, start, ptr);
|
||||
if (start) {
|
||||
/* set loop flag */
|
||||
for (ptr -= 5; ptr > buffer; ptr -= 5)
|
||||
*ptr |= 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pms2_buffer_to_schedule() - device buffer to schedule
|
||||
*
|
||||
* @buffer: 40 character buffer
|
||||
* @schedule: schedule
|
||||
*/
|
||||
void pms2_buffer_to_schedule(const unsigned char *buffer,
|
||||
struct plannif *schedule)
|
||||
{
|
||||
const unsigned char *ptr = buffer;
|
||||
uint32_t start, last, loop_ref, time;
|
||||
unsigned char action;
|
||||
int i;
|
||||
|
||||
plannif_reset(schedule);
|
||||
|
||||
ptr = pms2_read_block(&action, &start, ptr);
|
||||
schedule->actions[0].switchOn = 0;
|
||||
schedule->socket = (action - 1) / 3;
|
||||
schedule->timeStamp = start;
|
||||
last = start;
|
||||
for (i = 0; i < 7; ++i) {
|
||||
ptr = pms2_read_block(&action, &time, ptr);
|
||||
if (!i)
|
||||
loop_ref = time;
|
||||
if (action > 3)
|
||||
break;
|
||||
schedule->actions[i + 1].switchOn = action & 1;
|
||||
schedule->actions[i].timeForNext =
|
||||
((int32_t)time - (int32_t)last) / 60;
|
||||
last = time;
|
||||
}
|
||||
if (time)
|
||||
schedule->actions[i].timeForNext =
|
||||
((int32_t)loop_ref + time - (int32_t)last) / 60;
|
||||
}
|
|
@ -270,8 +270,7 @@ void plannif_display(const struct plannif *plan, int verbose,
|
|||
// now read all filled rows, except the possibly last "stop" row
|
||||
for (action = 0;
|
||||
action < sizeof(plan->actions) / sizeof(struct plannifAction) &&
|
||||
(plan->actions[action].switchOn != -1) &&
|
||||
(plan->actions[action].timeForNext > 0); ++action) {
|
||||
plan->actions[action].switchOn != -1; ++action) {
|
||||
date += 60 * plan->actions[action].timeForNext;
|
||||
if ((action + 1 < sizeof(plan->actions)/sizeof(struct plannifAction)) &&
|
||||
(plan->actions[action+1].switchOn != -1)) {
|
||||
|
@ -369,6 +368,7 @@ void usb_command_getplannif(usb_dev_handle *udev, int socket,
|
|||
int reqtype = 0x21 | USB_DIR_IN; /* request type */
|
||||
int req = 0x01;
|
||||
unsigned char buffer[0x28];
|
||||
unsigned int id;
|
||||
|
||||
if (usb_control_msg_tries(udev, /* handle */
|
||||
reqtype,
|
||||
|
@ -391,7 +391,11 @@ void usb_command_getplannif(usb_dev_handle *udev, int socket,
|
|||
printf("\n");
|
||||
// */
|
||||
|
||||
plannif_scanf(plan, buffer);
|
||||
id = get_id(usb_device(udev));
|
||||
if (id == PRODUCT_ID_SISPM_EG_PMS2)
|
||||
pms2_buffer_to_schedule(buffer, plan);
|
||||
else
|
||||
plannif_scanf(plan, buffer);
|
||||
}
|
||||
|
||||
// private : prints the buffer according to the schedule structure
|
||||
|
@ -458,11 +462,20 @@ void plannif_printf(const struct plannif *plan, unsigned char *buffer)
|
|||
// prepares the buffer according to plannif and sends it to the device
|
||||
void usb_command_setplannif(usb_dev_handle *udev, struct plannif* plan)
|
||||
{
|
||||
int reqtype=0x21; //USB_DIR_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE /*request type*/,
|
||||
int req=0x09;
|
||||
unsigned char buffer[0x27];
|
||||
int reqtype=0x21; //USB_DIR_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE /*request type*/,
|
||||
int req=0x09;
|
||||
unsigned char buffer_size = 0x27;
|
||||
unsigned char buffer[0x28];
|
||||
unsigned int id;
|
||||
|
||||
plannif_printf(plan, buffer);
|
||||
id = get_id(usb_device(udev));
|
||||
if (id == PRODUCT_ID_SISPM_EG_PMS2) {
|
||||
if (pms2_schedule_to_buffer(plan, buffer))
|
||||
exit(-2);
|
||||
} else {
|
||||
buffer_size = 0x27;
|
||||
plannif_printf(plan, buffer);
|
||||
}
|
||||
|
||||
/*// debug
|
||||
int n;
|
||||
|
@ -480,8 +493,8 @@ void usb_command_setplannif(usb_dev_handle *udev, struct plannif* plan)
|
|||
((0x03 << 8) | (3 * plan->socket)) + 1,
|
||||
0, /* index */
|
||||
(char *) buffer, /* bytes */
|
||||
0x27, /* size */
|
||||
5000) < 0x27 ) {
|
||||
buffer_size, /* size */
|
||||
5000) < buffer_size ) {
|
||||
fprintf(stderr, "Error performing requested action\n"
|
||||
"Libusb error string: %s\nTerminating\n", usb_strerror());
|
||||
usb_close (udev);
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#ifndef SISPM_CTL_H
|
||||
#define SISPM_CTL_H
|
||||
|
||||
#include <usb.h>
|
||||
|
||||
#define MAXGEMBIRD 32
|
||||
#define MAXANSWER 8192
|
||||
|
||||
|
@ -142,4 +144,9 @@ extern char *homedir;
|
|||
/* Base64 encoded user:password */
|
||||
extern char *secret;
|
||||
|
||||
int pms2_schedule_to_buffer(const struct plannif *schedule,
|
||||
unsigned char *buffer);
|
||||
void pms2_buffer_to_schedule(const unsigned char *buffer,
|
||||
struct plannif *schedule);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue