/* Projekt Convac-Ätzer
Henrik Haftmann, 161213
*/
#include "Convac.h"
#include "Display.h"
#include <string.h>
#include <stdlib.h>
RUN run;
static int startvalues[32]; // Platz für max. 32 Analogkanäle
//static byte prevvalues[12]; // Platz für max. 96 Digitalkanäle
int BEFEHL::getv() const{
switch (vlen()) {
case 0: return operation>>3&1; break; // 1 Bit aus Bit 3
case 1: return value[0]; break; // vorzeichenlos!
default: return *(int*)value; break;
//case 3: return *(long*)value; break;
}
}
bool BEFEHL::setv(int v) {
byte havebyte=vlen();
if (havebyte) { // Analogkanal
byte needbyte=1;
if (unsigned(v)>>8) ++needbyte;
if (havebyte!=needbyte) {
if (!GS::move(value+needbyte,value+havebyte)) return false; // kein Platz
operation=byte(operation&0xFC)|needbyte;
}
if (needbyte==1) value[0]=v; // vorzeichenlos!
else *(int*)value=v;
}else{ // Digitalkanal
if (unsigned(v)>1) return false; // Bits bleiben Bits
operation=operation&0xFB|byte(v)<<3; // 1 Bit nach Bit 3
}
return true;
}
bool BEFEHL::swapAna() {
byte op=operation;
byte no=op; // nop = Neuer Opcode
if (op&3) { // analog: umschalten zu digital
no&=~3; // ab jetzt digital
if (index>>3>DIGITAL::getMax()) index=0;
}else{
++no; // ab jetzt analog 1 Byte
if (index>ANALOG::getMax()) index=0; // auf etwas gültiges setzen!
}
if (!GS::move(value+(no&3),value+(op&3))) return false; // kein Platz
operation=no; // Platz geschaffen: eintragen
return true;
}
const char* BEFEHL::sprintKanal(bool dot) const{
if (vlen()||!dot) {
floatstr(index);
}else{
char*s=sbuf;
*s++='0'+(index>>5); // obere 3 Bits: Karten- = Anzeigeseitennummer
*s++='.';
*s++='0'+(index>>3&3); // mittlere 2 Bits: Bytenummer der Karte
*s++='.';
*s++='0'+(index&7); // untere 3 Bits: Bitnummer
*s=0;
}
return sbuf;
}
bool BEFEHL::sscanKanal(const char*s) { // liefert false bei Fehler, ansonsten wird .index gesetzt
unsigned v=strfloat(s);
if (vlen()) {
if (v>ANALOG::getMax()) return false; // zu groß, INF/NAN inkklusive
else
if (v>>3>DIGITAL::getMax()) return false; // zu groß (undefinierte Bits??)
}
index=v;
return true;
}
const char* BEFEHL::sprintVal(bool withUnit) const{
int v=getv();
if (vlen()) {
const ANALOG::scale_t*k=ANALOG::scale+index;
v=rounding_shr8((long)v*k->gain)+k->offset;
floatstr(v,k->nk);
if (withUnit) {
char*p=sbuf+strlen(sbuf); // Ende der Zahl
*p=' '; // mit Leerzeichen (außer bei °; kommt hier nicht vor)
strcpy(++p,k->unit); // Einheit (editierbar im RAM!) anhängen
}
}else floatstr(v);
return sbuf;
}
bool BEFEHL::sscanVal(const char*s) {
int v;
if (vlen()) {
const ANALOG::scale_t*k=ANALOG::scale+index;
v=strfloat(s,k->nk);
if (word(v-32767)<3) return false; // bei +INF, NAN oder -INF
v=rounding_div(long(v-k->offset)<<8,k->gain);
if (v<ANALOG::getMin(index)) return false;
if (v>ANALOG::getMax(index)) return false;
}else{
v=strfloat(s);
}
return setv(v);
}
// Information über den Befehl preisgeben
void BEFEHL::print(byte i) const{
d.print(
operation&0x60?F("Wenn") // Eingaben
:vlen()||operation&OP_D1?F("Setze")
:F("Lösche"));
d.X+=3;
d.print(vlen()?F("Wert"):F("Bit"));
d.print('[');
d.print(sprintKanal());
d.print(']');
d.X+=3;
if (operation&0x60&&operation&0x10) d.print(F("Δ "));
char sign=swap(operation);
if (vlen()) sign^=ANALOG::scale[index].gain>>8; // Größer/kleiner in der Anzeige umdrehen bei negativem Gain
d.print(
operation&0x60
?vlen()
?sign<0?F("größer"):F("kleiner") // lesen analog
:operation&0x10
?F("wechselt zu")
:F("gleich")
:F("auf"));
d.X+=3;
d.print(sprintVal(true));
if (operation&0xE0) d.print(F(" dann "));
if (operation&0x60) {
d.print(
(operation&0x60)==OP_CNT?F("weiter")
:(operation&0x60)==OP_DNT?F("Start verhindern")
:F("Abbruch"));
if (operation&OP_LOG) d.print(F(" + "));
}
if (operation&OP_LOG) {
d.print(F("Log"));
}
}
/* Abarbeitungstabelle
Schritt S0 Sn
Op. Rezept läuft nicht Rezept läuft
ausführen? Folge ausführen? Folge
Globale Sich. OUT ja - nein -
CNT ja S1 # nein -
DNT ja S0 nein -
BRK ja S0 ja S0
Rezept-Sich. OUT ja - nein -
CNT nein - ja S+1 #
DNT ja S0 nein -
BRK ja S0 ja S0
Rezept-Schritt OUT - - ja -
CNT - - ja S+1 #
DNT - - ja S0 (am Ende)
BRK - - ja S0 (sofort)
# Wenn alle zustimmen
*/
// Einzelaktion anwenden:
// * Digitalausgabe (nur) bei percentage==0
// * Keine Analogausgabe bei percentage==0, stattdessen Merken des aktuellen Analogwertes
// * Analogwertausgabe prozentual linear je nach percentage bis percentage==255
// * Digital- und Analogeingabe und Prüfung bei jedem Aufruf
// * {Flankendetektion je nach zuletzt gelesenem Wert}
void BEFEHL::apply() const{
int v=getv();
if (operation&0x60) { // Eingabe-Operation mit anschließender Prüfung
if ((operation&0x60)==OP_CNT) run.status|=1; // Min. ein Befehl mit Continue
if (vlen()) { // analog
int cur=ANALOG::getIo(index);
if (operation&OP_D1) {
if (cur>v) return; // Datenbit gesetzt: "wahr" ist "größer"
}else{
if (cur<v) return; // Datenbit gesetzt: "wahr" ist "kleiner"
}
}else{ // digital
bool bit=DIGITAL::getBit(index);
if (bit==(bool)v) return; // immer prüfen: "wahr" ist "gleich"
}
// An dieser Stelle sind Eingaben FALSCH geworden: Aktionen als Bitmaske kombinieren
byte b=shlb(1,swap(operation)>>1&3); // da kommt nur 0010, 0100 oder 1000 raus
run.status|=b; // OR-verknüpfen
if (operation&OP_LOG) {
ALARM::generate(this); // jeder Befehl einzeln
run.status|=0x80; // für Piepton
}
}else{ // Ausgabe-Operation
if (vlen()) { // analog
if (!run.percentage) startvalues[index]=ANALOG::getIo(index);
else{
if (run.percentage<0xFF) {
v-=startvalues[index]; // vzb. Differenz
v=rounding_shr8((long)v*run.percentage); // Rampe realisieren
v+=startvalues[index];
}
ANALOG::setIo(index,v);
}
}else{ // digital
if (!run.percentage) DIGITAL::setBit(index,v); // am Anfang setzen
}
}
}
void BEFEHL::applyMult(byte n) const{
const BEFEHL*e=this; // <this> ist leider kein lvalue
for(;n;--n) {
e->apply();
e=e->fin(); // Objektzeiger zum nächsten Element vorrücken
}
}
void SCHRIT::print(byte i) const{
if (i) {
d.print(F("Schritt "));
d.print(floatstr(i));
d.print(F(" über "));
d.print(floatstr(ms10,2));
d.print(F(" s")); // Sekunden
}else{
d.print(F("Ständige Prüfung"));
}
d.print(F(", "));
d.print(floatstr(n.c));
d.print(F(" Befehl"));
if (n.c!=1) d.print('e');
}
/*
void SCHRIT::apply() const{
// run.status=0;
be->applyMult(n.c); // ganze Liste durchgehen
run.applyStatus();
if (!run.status // keine Sonderbehandlung?
&& allowTimedNext
&& run.percentage==0xFF) run.next(); // Zeit herum? Dann Weiter!
}
*/
void REZEPT::print(byte i) const{
d.print(F("Rezept "));
d.print(floatstr(i+1)); // kein Name, 1-basiert anzeigen
d.print(':');
d.X+=3;
d.print(floatstr(n.c-1)); // Sicherheit nicht mitzählen
d.print(F(" Schritt"));
if (n.c) {
d.print(F("e, ID = "));
d.print(floatstr(st[0].ms10));
}
}
void RUN::applyStatus() {
if (run.status&1) { // Mindestens ein Continue-Befehl?
run.status&=~1;
if (run.status&6) { // Tatsächliches Continue verboten?
if (run.percentage==0xFF) { // Am Ende der Zeit?
ALARM::generate(run.st); // Timeout generieren
run.status=0x88; // Abbruch erzwingen
}
}else{ // Mindestens ein Continue-Befehl, und ALLE wahr
run.next(); // nächster Schritt (vorzeitig)
return;
}
}
if (run.status&0x80) beep(64,3); // Log-Einträge mit Ton
run.status&=~0x80;
if (run.status&8) { // Abbruch
run.status&=~8;
if (run.aktion) {
byte last=run.re->n.c-1;
if (run.aktion==last) run.next(); // zu Ende führen
else run.next(last); // zum letzten Schritt springen
}
return;
}
}
void RUN::init() {
// DIGITAL::getInputs(prevvalues);
run.time=0;
run.re=curRezept();
eedata.gs.st.apply(); // abfragen mit 0%
}
void RUN::next(byte n) {
if (run.aktion) xorAktionMarker(); // Zeigerpfeil aus Liste weg
else init();
if (!n) n=run.aktion+1; // wenn n!=0 gegeben, zu bestimmtem Schritt springen
if (n>=run.re->n.c) {
run.aktion=0; // Ende des Rezepts
ANI::show(); // wegnehmen
}else{
run.aktion=n;
ANI::show(); // Zahl aktualisieren
xorAktionMarker(); // Zeigerpfeil in Liste setzen
run.st=curSchrit(); // Zeiger aktualisieren
// run.percentage=0; // Null auch für den nächsten Sicherheitsdurchlauf
// run.re->st[0].apply(false);
// run.st->apply(true); // Erster Durchlauf
}
}
void RUN::start(byte rez) {
if (rez>=rl->nRezept) return; // Fehler
if (run.aktion) return; // Fehler
run.rezept=rez;
run.next(); // Nur vor next() muss run.a nicht initialisiert werden
}
void RUN::tick() {
run.re=curRezept(); // Zeiger wiederherstellen
run.st=curSchrit(); // (können sich durch Editieren verändern)
run.percentage=0xFF;
run.status=0;
eedata.gs.st.apply(); // immer abfragen
if (run.aktion) {
ANI::animate();
run.time+=10;
if (run.time>=run.st->ms10) { // Zur nächsten Aktion?
run.time-=run.st->ms10;
}else{
run.percentage=rounding_div((long)run.time<<8,run.st->ms10);
}
run.re->st[0].apply();
run.st->apply();
}
run.applyStatus();
if (run.aktion && run.percentage==0xFF) run.next();
}
/***************
* Alarm-Liste *
***************/
ALARM ALARM::alarm[10];
ALARM*ALARM::generate(const void*id) {
ALARM*a=find(id);
if (!a) {
for (a=alarm; a<alarm+elemof(alarm); a++) if (!a->occ) goto found;
a=oldest(); a->occ=0;
found:
a->p=id;
}
a->time=ticker.w;
a->occ++;
return a;
}
ALARM*ALARM::find(const void*id) {
for (auto a=alarm; a<alarm+elemof(alarm); a++) if (a->p==id) return a;
return 0;
}
ALARM*ALARM::extreme(bool min_age) {
ALARM*a=0; // kann NULL liefern (nur bei leerer Liste)
word w=min_age?0xFFFF:0;
for (auto b=alarm; b<alarm+elemof(alarm); b++) if (b->occ) {
word age=ticker.w-b->time;
if (min_age && w>=age || !min_age && w<=age) {a=b; w=age;}
}
return a;
}
void ALARM::clearAll() {
for (auto a=alarm; a<alarm+elemof(alarm);a++) a->clear();
}
byte ALARM::n() {
byte n=0;
for (const ALARM*a=alarm; a<alarm+elemof(alarm);a++) if (a->occ) ++n;
return n;
}
ALARM*ALARM::nth(byte i){
auto a=alarm; ++i;
for (;;a++) if (a->occ && !--i) break;
return a;
}
void ALARM::move(const void*dst,const void*src) {
for (auto a=alarm;a<alarm+elemof(alarm);a++) if (a->occ) {
if (word(a->p)>=(word)src) *(word*)&a->p+=(word)dst-(word)src;
}
}
void ALARM::print() { // Tabellen-Ausgabe rechtsbündig
floatstr(time,2); // Zeitstempel
printSbufRight(30);
floatstr(occ); // Anzahl des Auftretens
printSbufRight(50);
ITER iter(p); // iteriert Liste bis zur gegebenen Adresse
if (iter.type!=2) {
d.X=60;
// d.print(utox(iter.type,2));
d.print(utox(p));
}else{
if (iter.ire==0xFF){
d.X=55;
d.print(F("gS"));
}else{
floatstr(iter.ire+1); // Rezept
printSbufRight(60);
floatstr(iter.ist); // Schritt im Rezept (0 = Sicherheit)
printSbufRight(70);
}
floatstr(iter.ibe+1); // Befehl im Schritt
printSbufRight(80);
d.X=85;
be->print(iter.ibe); // Befehlstext
}
}
// Zeiger-Such-Konstruktor
ITER::ITER(const void*id) {
byte nSchrit=1;
initgs(); ire=0xFF; goto innen;
aussen:
init();
while (ire<rl->nRezept) {
// type=0;
// if (p>=id) return;
nSchrit=re->n.c;
st=re->st0();
innen:
for (ist=0;ist<nSchrit;ist++) {
// type=1;
// if (p>=id) return;
byte nBefehl=st->n.c;
be=st->be0();
type=2;
for (ibe=0;ibe<nBefehl;ibe++) {
if (p==id) return;
be=be->fin();
}// am Ende dieser Schleife steht der Zeiger auf SCHRIT*
}// am Ende dieser Schleife steht der Zeiger auf REZEPT*
if (!++ire) goto aussen; // weiter mit Rezeptliste (nRezept überspringen)
}// am Ende dieser Schleife steht der Zeiger am Ende der Rezeptliste
type=0xFF;
}
Vorgefundene Kodierung: UTF-8 | 0
|