/* Projekt: maweig-Motor
Teil: Terminal-Steuerung und -Darstellung
Highlight: Mit Farben und Braille-Grafik
191004 erstellt
*/
#include "Settings.h"
#include "regdef2.h"
#include <cstring> // memset
#include <limits>
#include "DLOG_4CH.h" // class Datalog
/************************
** Diagrammerstellung **
************************/
template<class T=int> union Point{
struct{
T x,y;
};
T A[2];
T&operator[](int i) {return A[i];}
const T&operator[](int i) const{return A[i];}
};
template<class T=int> union Rect{
struct{
T l,t,r,b;
};
struct{
Point<T> lt,rb;
};
T A[4];
T w() const {return r-l;}
T h() const {return b-t;}
T&operator[](int i) {return A[i];}
const T&operator[](int i) const{return A[i];}
};
// T kann leider kein Tupel sein
template<class T> void setMin(T&min,T v) {if (min>v) min=v;}
template<class T> void setMax(T&max,T v) {if (max<v) max=v;}
template<class T> class Range{
public:
T min,max;
Range() {reset();}
Range(T startfrom) {reset(startfrom);}
Range(Range<T>&src) {reset(src);}
void reset() {reset(std::numeric_limits<T>::max(),std::numeric_limits<T>::min());}
void reset(const T startfrom) {max=min=startfrom;}
void reset(const T _min, const T _max) {min=_min, max=_max;}
void reset(const Range<T>&src) {memcpy(this,&src,sizeof*this);}
void expand(const T v) {expand(v,v);}
void expand(const T _min, const T _max) {setMin(min,_min); setMax(max,_max);}
void expand(const Range<T>&src) {expand(src.min,src.max);}
void makeSymmetric(T center=0) {setMin(min,center-max+center); setMax(max,center-min+center);}
const T getDelta() const {return max-min;}
};
template<class TX,class TY> struct Scale{
virtual TY scale(TX v) const =0;
virtual TX unscale(TY v) const =0;
TY operator()(TX v)const{return scale(v);} // Kurzform von scale()
TX operator[](TY v)const{return unscale(v);} // Kurzform von unscale()
};
template<class TX,class TY> struct NoScale:public Scale<TX,TY>{
virtual TY scale(TX v) const {return TY(v)};
virtual TX unscale(TY v) const {return TX(v)};
};
template<class TX,class TY,class TF=float> struct LinScale:public Scale<TX,TY>{
TF factor;
TY offset;
LinScale(TF f=1,TY o=0):factor(f),offset(o) {}
virtual TY scale(TX v) const {return TY(v*factor)+offset;}
virtual TX unscale(TY v) const {return TX((v-offset)/factor);}
void TwoPointSetup(TX x1, TY y1, TX x2, TY y2) {
TF N=TF(x2)-TF(x1); // hier: Integer-Überläufe vermeiden
if (N) factor = (TF(y2)-TF(y1))/N; // ggf. unverändert oder 1 belassen
offset = y1-TY(factor*x1);
}
};
static const wchar_t braille[8]={0x2801,0x2808,0x2802,0x2810,0x2804,0x2820,0x2840,0x2880};
//static const wchar_t braille[]=L"⠁⠈⠂⠐⠄⠠⡀⢀"; // geht nicht!
template<unsigned W,unsigned H> class ScreenBuffer {
wchar_t c[W*H]; // Zeichen
unsigned a[W*H]; // Attribute (Farben)
public:
static const unsigned _W=W,_H=H;
void setpixel(int x,int y,unsigned attr) {
if ((unsigned)x>=W*2) return;
if ((unsigned)y>=H*4) return;
int ofs=(x>>1)+(y>>2)*W; // Zeichen- und Attribut-Offset
x = (x&1 | y<<1)&7; // 0 1 0 3
// 2 3 1 4
// 4 5 2 5
// 6 7 6 7
a[ofs]=attr; // Attribut gnadenlos überschreiben (erstmal)
wchar_t chr=c[ofs];
if ((chr&0xFF00) != 0x2800) chr=0; // Braille-Zeichen festlegen
chr|=braille[x];
c[ofs]=chr;
}
void outchar(int x, int y, wchar_t chr, unsigned attr) {
if ((unsigned)x>=W*2) return;
if ((unsigned)y>=H*4) return;
int ofs=(x>>1)+(y>>2)*W; // Zeichen- und Attribut-Offset
c[ofs]=chr;
a[ofs]=attr;
}
int outstring(int x, int y, const wchar_t*str, int len, unsigned attr) {
if ((unsigned)x>=W*2) return;
if ((unsigned)y>=H*4) return;
int ofs=(x>>1)+(y>>2)*W; // Zeichen- und Attribut-Offset
if (len<0) len=wcslen(str);
setMin(len,W-(x>>1)); // Nur bis zum rechten Rand, nicht in Folgezeile
memcpy(c+ofs,str,len); // hier: kopiert wchar_t
memset(a+ofs,attr,len); // hier: füllt unsigned
return len;
}
void clear() {memset(c,' ',W*H);memset(a,7,W*H);}
void outscreen() {
unsigned ca=7;
for (int y=0; y<H; y++) {
for (int x=0; x<W; x++) {
wchar_t cc=c[x+y*W];
if (cc!=' ') {
unsigned aa=a[x+y*W];
if (ca!=aa) {
ca=aa;
unsigned vfarbe=(ca&7)+(ca&8?90:30);
unsigned hfarbe=(ca>>4&7)+(ca&128?100:40);
com0.printf(L"\e[%u;%um",vfarbe,hfarbe);
}
}
// TODO: Sonstige Attribute (Unterstreichen u.ä.); sprintf mit Attributen
com0.send(cc);
}
com0.send(L"\r\n");
}
}
};
const unsigned GRAPH_H=13;
struct DC{
ScreenBuffer<80,GRAPH_H>&bmp;
bool validpos;
Point<> pos;
unsigned attr;
DC(ScreenBuffer<80,GRAPH_H>&surface):bmp(surface),validpos(false) {}
void MoveTo(int x, int y) {pos.x=x; pos.y=y;validpos=true;}
void LineTo(int x, int y) {
if (validpos) Line(pos.x,pos.y,x,y);
MoveTo(x,y);
}
void Line(int x0,int y0,int x1,int y1) { // aus Wikipedia
if (labs(long(x1)-x0)>=16384) return;
if (labs(long(y1)-y0)>=16384) return;
int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
int dy = -abs(y1-y0), sy = y0<y1 ? 1 : -1;
int err = dx+dy; /* error value e_xy */
while(x0!=x1 || y0!=y1) { // zeichnet letztes Pixel nicht mit
bmp.setpixel(x0,y0,attr);
int e2 = 2*err;
if (e2 > dy) { err += dy; x0 += sx; }
if (e2 < dx) { err += dx; y0 += sy; }
}
}
};
// Dort ist der Datenpuffer
extern DataLog<int,4,160,1> logger;
static const int channel_of_interest = 1;
static void outDiagram() {
// Und hier die Bitmap
ScreenBuffer<80,GRAPH_H>*bmp=new ScreenBuffer<80,GRAPH_H>;
bmp->clear();
for (int x=0; x<160; x+=2) bmp->outchar(x,GRAPH_H*2,L'─',8);
// Rahmensymbol für Nulllinie in dunkelgrau
Range<int>minmax(0);
// int fill=rrdb.getminmax(minmax);
int fill=logger.rewind();
for (int i=0; i<fill; i++) minmax.expand(logger.get()[channel_of_interest]);
logger.rewind();
minmax.makeSymmetric();
LinScale<int,int> yscale;
yscale.TwoPointSetup(minmax.min,GRAPH_H*4-1,minmax.max,0);
DC dc(*bmp);
dc.attr=11; // https://en.wikipedia.org/wiki/ANSI_escape_code#SGR
for (int i=0; i<fill; i++) {
dc.LineTo(i,yscale(logger.get()[channel_of_interest]));
}
logger.clear(); // nächste Aufnahme mit 10 kSa/s vorbereiten
bmp->outscreen();
delete bmp;
}
/************************
** Text/Zahlenausgabe **
************************/
static void outAdcValues(volatile ADC_RESULT_REGSA®s) {
com0.printf(L"ADC%u\t",int(®s-AdcResultRegsA));
for (int i=0; i<6; i++) {
com0.printf(L"%6j",regs.RESULT[i]);
}
com0.send(L"\r\n");
}
static void outAdcValues() {
for (int i=0; i<3; i++) outAdcValues(AdcResultRegsA[i]); // Adca bis Adcc
}
static void outAmcValues() {
extern unsigned Amc1210Okay[4];
extern unsigned Amc1210IR,Amc1210SR;
extern int Amc1210Data[4]; // in MotorControl.cpp
DINT;
unsigned ir=Amc1210IR; Amc1210IR=0; // bei gesperrten Interrupts lokale Kopien erstellen
unsigned sr=Amc1210SR; Amc1210SR=0;
int dat[4];
memcpy(dat,Amc1210Data,sizeof dat);
unsigned okay[4];
memcpy(okay,Amc1210Okay,sizeof okay);
memset(Amc1210Okay,0,sizeof Amc1210Okay);
EINT;
com0.printf(L"AmcSpiA\t");
for (int i=0; i<4; i++) com0.printf(L"%6j", dat[i]); // alle 4 Kanäle
// Besser wäre es, hier Effektiv- und Mittelwerte auszuspucken!
com0.printf(L" %cR = %#04X",'I',ir);
com0.printf(L" %cR = %#04X",'S',sr);
com0.send(L"\r\n");
com0.printf(L"Versuche\t");
for(int versuch=0; versuch<4; versuch++)
com0.printf(L" %u.:%u ",versuch+1,okay[versuch]);
com0.send(L"\r\n");
// rrdb.put(&dat[1].value); // Da liegt der Analogwert an
}
static void outSdValues(volatile SDFM_REGSA®s) {
com0.printf(L"Sd%u\t",int(®s-SdfmRegsA));
for (int i=0; i<4; i++) {
com0.printf(L"%`12lj",regs[i].I32);
}
com0.send(L"\r\n");
}
void Terminal::Update() {
float T = Adc::myGetTemperatureC(AdcaResultRegs.ADCRESULT0);
com0.printf(L"\rT = \e[1m%,1f\e[m °C ",T);
com0.send(L"\e[s" L"\e[?25l" L"\r\n"); // Cursor merken und 1 Zeile runter
outAdcValues();
outAmcValues();
outSdValues(Sdfm1RegsA);
outSdValues(Sdfm2RegsA);
outDiagram();
// com0.printf(L"Gruppierung? %`,5f",12345.67890);
com0.send(L"\e[?25h" L"\e[u"); // Cursor zurück
// Eingegebene Zeichen (kurz) darstellen
wchar_t buf[16];
int l=com0.recv(buf);
for (int i=0; i<l; i++) {
wchar_t c=buf[i];
static const wchar_t tr7[]=L"abtrvfn";
if (7<=c && c<=13) c=tr7[c-7];
else if (c==L'\e') c=L'e';
else if (c==L'\\');
else if (c<32) {
com0.printf(L"\\x%02X",c); continue;
}else{
com0.send(c); continue;
}
com0.send(L'\\');
com0.send(c);
}
}
Detected encoding: ASCII (7 bit) | 8
|