2022-05-24 Fred Gleason <fredg@paravelsystems.com>

* Added an 'RDRange' class.
	* Added support for range specifications to the 'line' parameter
	of the 'GPI Enable' ['GE'] RML.

Signed-off-by: Fred Gleason <fredg@paravelsystems.com>
This commit is contained in:
Fred Gleason
2022-05-24 11:52:27 -04:00
parent b2e1c2926f
commit 0c21db5f5c
6 changed files with 273 additions and 30 deletions

View File

@@ -23171,3 +23171,7 @@
* Fixed a bug in the 'LiveWireLwrpGpio::gpiQuantity()' and
'LiveWireLwrpGpio::gpoQuantity()' methods where they returned slot
rather than line quantities.
2022-05-24 Fred Gleason <fredg@paravelsystems.com>
* Added an 'RDRange' class.
* Added support for range specifications to the 'line' parameter
of the 'GPI Enable' ['GE'] RML.

View File

@@ -767,11 +767,11 @@
<userinput>GE
<replaceable>matrix</replaceable>
<replaceable>type</replaceable>
<replaceable>gpinum</replaceable>
<replaceable>gpinum-spec</replaceable>
<replaceable>state</replaceable>!</userinput>
</para>
<para>
Enable or disable the GPI line of type <replaceable>type</replaceable>
Enable or disable the GPI line(s) of type <replaceable>type</replaceable>
indicated by <replaceable>gpinum</replaceable> on matrix
<replaceable>matrix</replaceable>. Possible types are:
</para>
@@ -787,6 +787,97 @@
</varlistentry>
</variablelist>
</para>
<para>
The <replaceable>gpinum-spec</replaceable> can be an integer value
to indicate a single GPI line or a range specification to indicate
multiple lines. A range specification has the following format:
</para>
<para>
[<replaceable>first-value</replaceable>]:[<replaceable>last-value</replaceable>]
</para>
<para>
All values in a range specification are inclusive. Some examples:
</para>
<para>
<table xml:id="sect.rml.gpi_enable__ge_.range_examples" frame="all" pgwide="0">
<title>Range Specification Examples</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<colspec colname="Specification" colwidth="1.0*"/>
<colspec colname="Meaning" colwidth="10.0*"/>
<tbody>
<row>
<entry align="center">
1:10
</entry>
<entry>
Lines 1 through 10, inclusive
</entry>
</row>
<row>
<entry align="center">
5:10
</entry>
<entry>
Lines 5 through 10, inclusive
</entry>
</row>
<row>
<entry align="center">
7:7
</entry>
<entry>
Line 7
</entry>
</row>
<row>
<entry align="center">
:10
</entry>
<entry>
Lines 1 through 10, inclusive
</entry>
</row>
<row>
<entry align="center">
1:
</entry>
<entry>
Lines 1 onward, inclusive
</entry>
</row>
<row>
<entry align="center">
:
</entry>
<entry>
All lines in the specified matrix
</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
<note>
<title>Note for Python programmers</title>
Python programmers in the audience may be noticing similarities between
range specifications and
<link xlink:href="https://stackoverflow.com/questions/509211/understanding-slicing">Python slices</link>.
The concepts are similar, but there are differences! Specifically:
<itemizedlist>
<listitem>
All values in range specifications are <emphasis>inclusive</emphasis>,
whereas the <replaceable>stop</replaceable> parameter in a slice is
<code>last_value+1</code>.
</listitem>
<listitem>
There is no <replaceable>step</replaceable> parameter.
</listitem>
<listitem>
All parameter values must be positive integers --i.e. zero- and
negative-valued parameters are not supported.
</listitem>
</itemizedlist>
</note>
</sect2>
<sect2 xml:id="sect.rml.gpi_enable__ge___old_format__deprecated_">
@@ -809,14 +900,21 @@
<para>
<userinput>GE
<replaceable>matrix</replaceable>
<replaceable>gpinum</replaceable>
<replaceable>gpinum-spec</replaceable>
<replaceable>state</replaceable>!</userinput>
</para>
<para>
Enable or disable the GPI line indicated by
<replaceable>gpinum</replaceable> on matrix
Enable or disable the GPI line(s) indicated by
<replaceable>gpinum-spec</replaceable> on matrix
<replaceable>matrix</replaceable>.
</para>
<para>
The <replaceable>gpinum-spec</replaceable> can be an integer value
to indicate a single GPI line or a range specification to indicate
multiple lines. See
<xref linkend="sect.rml.gpi_enable__ge_.range_examples" /> above
for examples of range specifications.
</para>
</sect2>
<sect2 xml:id="sect.rml.gpi_set__gi_">

View File

@@ -219,6 +219,7 @@ dist_librd_la_SOURCES = dbversion.h\
rdprofile.cpp rdprofile.h\
rdpushbutton.cpp rdpushbutton.h\
rdpypadlistmodel.cpp rdpypadlistmodel.h\
rdrange.cpp rdrange.h\
rdrecording.cpp rdrecording.h\
rdrehash.cpp rdrehash.h\
rdrenderer.cpp rdrenderer.h\

88
lib/rdrange.cpp Normal file
View File

@@ -0,0 +1,88 @@
// rdrange.cpp
//
// Implement Rivendell range specifications
//
// (C) Copyright 2022 Fred Gleason <fredg@paravelsystems.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include <QStringList>
#include "rdrange.h"
RDRange::RDRange(int max_end)
{
d_max_end=max_end;
d_start=0;
d_end=0;
}
int RDRange::start() const
{
return d_start;
}
int RDRange::end() const
{
return d_end;
}
bool RDRange::parse(const QString &str)
{
QStringList f0=str.split(":",QString::KeepEmptyParts);
bool ok1=false;
bool ok2=false;
switch(f0.size()) {
case 1:
d_start=f0.at(0).toInt(&ok1);
d_end=f0.at(0).toInt(&ok2);
if((!ok1)||(!ok2)||(d_start<=0)||(d_start>d_max_end)) {
d_start=0;
d_end=0;
return false;
}
return true;
case 2:
if(f0.at(0).isEmpty()) {
d_start=1;
ok1=true;
}
else {
d_start=f0.at(0).toInt(&ok1);
}
if(f0.at(1).isEmpty()) {
d_end=d_max_end;
ok2=true;
}
else {
d_end=f0.at(1).toInt(&ok2);
}
if((!ok1)||(!ok2)||(d_start<=0)||(d_end>d_max_end)||(d_start>d_end)) {
d_start=0;
d_end=0;
return false;
}
return true;
}
d_start=0;
d_end=0;
return false;
}

41
lib/rdrange.h Normal file
View File

@@ -0,0 +1,41 @@
// rdrange.h
//
// Implement Rivendell range specifications
//
// (C) Copyright 2022 Fred Gleason <fredg@paravelsystems.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#ifndef RDRANGE_H
#define RDRANGE_H
#include <QString>
class RDRange
{
public:
RDRange(int max_end);
int start() const;
int end() const;
bool parse(const QString &str);
private:
int d_start;
int d_end;
int d_max_end;
};
#endif // RDRANGE_H

View File

@@ -32,6 +32,7 @@
#include <rdescape_string.h>
#include <rdmatrix.h>
#include <rdpaths.h>
#include <rdrange.h>
#include <rdtty.h>
#include "ripcd.h"
@@ -238,6 +239,7 @@ void MainObject::RunLocalMacros(RDMacro *rml_in)
QString str;
QString sql;
QString cmd;
RDRange *range=NULL;
RDSqlQuery *q;
QHostAddress addr;
RDUser *rduser;
@@ -348,12 +350,22 @@ void MainObject::RunLocalMacros(RDMacro *rml_in)
return;
}
matrix_num=rml->arg(0).toInt();
if((ripcd_switcher[matrix_num]==NULL)||
(rml->arg(3).toInt()<0)||(rml->arg(3).toInt()>1)) {
if(rml->echoRequested()) {
rml->acknowledge(false);
sendRml(rml);
}
return;
}
if(rml->arg(1).toLower()=="i") {
gpio_type=RDMatrix::GpioInput;
range=new RDRange(ripcd_switcher[matrix_num]->gpiQuantity());
}
else {
if(rml->arg(1).toLower()=="o") {
gpio_type=RDMatrix::GpioOutput;
range=new RDRange(ripcd_switcher[matrix_num]->gpoQuantity());
}
else {
if(rml->echoRequested()) {
@@ -363,39 +375,38 @@ void MainObject::RunLocalMacros(RDMacro *rml_in)
return;
}
}
gpi=rml->arg(2).toInt()-1;
if((ripcd_switcher[matrix_num]==NULL)||
(gpi>(MAX_GPIO_PINS-1))||
(gpi<0)||
(rml->arg(3).toInt()<0)||(rml->arg(3).toInt()>1)) {
if(!range->parse(rml->arg(2))) {
delete range;
if(rml->echoRequested()) {
rml->acknowledge(false);
sendRml(rml);
}
return;
}
switch(gpio_type) {
case RDMatrix::GpioInput:
if(rml->arg(3).toInt()==1) {
ripcd_gpi_mask[matrix_num][gpi]=true;
BroadcastCommand(QString::asprintf("GM %d %d 1!",matrix_num,gpi));
}
else {
ripcd_gpi_mask[matrix_num][gpi]=false;
BroadcastCommand(QString::asprintf("GM %d %d 0!",matrix_num,gpi));
}
break;
for(int i=range->start()-1;i<range->end();i++) {
switch(gpio_type) {
case RDMatrix::GpioInput:
if(rml->arg(3).toInt()==1) {
ripcd_gpi_mask[matrix_num][i]=true;
BroadcastCommand(QString::asprintf("GM %d %d 1!",matrix_num,i));
}
else {
ripcd_gpi_mask[matrix_num][i]=false;
BroadcastCommand(QString::asprintf("GM %d %d 0!",matrix_num,i));
}
break;
case RDMatrix::GpioOutput:
if(rml->arg(3).toInt()==1) {
ripcd_gpo_mask[matrix_num][gpi]=true;
BroadcastCommand(QString::asprintf("GN %d %d 1!",matrix_num,gpi));
case RDMatrix::GpioOutput:
if(rml->arg(3).toInt()==1) {
ripcd_gpo_mask[matrix_num][i]=true;
BroadcastCommand(QString::asprintf("GN %d %d 1!",matrix_num,i));
}
else {
ripcd_gpo_mask[matrix_num][i]=false;
BroadcastCommand(QString::asprintf("GN %d %d 0!",matrix_num,i));
}
break;
}
else {
ripcd_gpo_mask[matrix_num][gpi]=false;
BroadcastCommand(QString::asprintf("GN %d %d 0!",matrix_num,gpi));
}
break;
}
if(rml->echoRequested()) {
rml->acknowledge(true);