PLC Ladder logic - problematic sequence using two potentiometer - controls

I've spent many hours and and a ton of paper sketching and I haven't been able to stumble upon anything to get me past this problem.
We got two tanks filled with water and two pumps between them. We need to automatically push the water to the second tank using these pumps if h>hmax (Tank 1) and H
I can't figure out one thing. First sequence assumes that if H>Hmax the pump 1 or 2 will work (they can't work simultaneously). When the water is pumped to second tank and in first tank will fall, the sequence is repeated after the water is replenished in first tank. I can't figure out how to make automatic change in pumps using only potentiometer after each sequence. What i need to do more is to include failure of one of the pump. I'm dealing with this problem for almost a week.

Hope this is what you mean. I wrote it as an "working" pseudo code.
In real life this code is not the best, cause from the moment on you have more then 2 pumps it should be complicated, but this one is very detailed.
Also an emergency switch is missing (so a button someone can push if he has to stop the pumps manually).
Better would be to use some function_blocks and methods...
declaration:
VAR
//INPUT
levelTank1 AT %I* : INT; //potentiometer 1
levelTank2 AT %I* : INT; //potentiometer 2
pump1Malfunction AT %I* : BOOL; //TRUE=pump has no malfunction || FALSE=malfunction
pump2Malfunction AT %I* : BOOL;
//OUTPUT
pump1Operation AT %Q* : BOOL; //TRUE=pump should run
pump2Operation AT %Q* : BOOL;
lastPumpInOperation : BYTE := 1;
nState : UINT := 0;
END_VAR
Somewhere you should define the min and max levvels, I did it as a constant value:
VAR CONSTANT
tank1Hmax : INT := 32000;
tank1Hmin : INT := 5000;
tank2Hmax : INT := 25000;
tank2Hmin : INT := 2000;
END_VAR
As remark: the analog inputs are usually INT values, so max. level equals 32767
And now the program:
CASE nState OF
0: //wait till tank 1 is filled and make sure tank2 is not overcharged
IF (levelTank1 >= tank1Hmax) AND NOT (levelTank2 >= tank2Hmax) THEN
nState := 1;
END_IF
1: //start one pump
//first check worst case - both pumps are broken
IF NOT pump1Malfunction AND NOT pump2Malfunction THEN
nState := 100; //emergency
//last time pump1 was used, and now pump 2 should start when it has no malfunction
ELSIF lastPumpInOperation = 1 AND pump2Malfunction THEN
pump2Operation := TRUE;
lastPumpInOperation := 2;
nState := 2;
//last time pump2 was used, and now pump 1 should start when it has no malfunction
ELSIF lastPumpInOperation = 2 AND pump1Malfunction THEN
pump1Operation := TRUE;
pump2Operation := FALSE;
lastPumpInOperation := 1;
nState := 2;
//now we have to define what happens when one pump is in malfunction
ELSIF NOT pump1Malfunction THEN
pump2Operation := TRUE;
pump1Operation := FALSE;
lastPumpInOperation := 2;
nState := 2;
ELSIF NOT pump2Malfunction THEN
pump1Operation := TRUE;
lastPumpInOperation := 1;
nState := 2;
END_IF
2: //pump operation till tank1 is "empty", tank2 is "full" or pump failure occurs
IF (levelTank1 <= tank1Hmin) OR (levelTank2 >= tank2Hmax) THEN
nState := 3; //stop pumps
ELSIF (pump1Operation AND NOT pump1Malfunction) THEN //pump has gone in malfunction during operation
nState := 1; //go back to step2 to start another pump
ELSIF (pump2Operation AND NOT pump2Malfunction) THEN
nState := 1;
END_IF
3: //stop pumps
pump1Operation := FALSE;
pump2Operation := FALSE;
nState := 0;
100: //emergency
//do what you can do - close the valve to tank1 or run as fast as you can
pump1Operation := FALSE;
pump2Operation := FALSE;
IF (pump1Malfunction OR pump2Malfunction) THEN
nState := 0;
END_IF
END_CASE

Related

VHDL calculations inside a function

I want to write a function that returns mod 100.
What's wrong with code below?
subtype int is integer range 0 to 255;
function div(val : int) return int is
variable result : int;
variable number : int;
begin
result := 0;
number := val;
while number >= 100 loop
result := result + 1;
number := number - 100;
end loop;
return result;
end div;
Edit: extend the answer to be more understandable.
In your function you return the quotient that is counted in result, not the remainder that is left in number. But this is what I would expect from a function named div().
If you want to return the remainder, you can use the same code, but change the last line into
return number;
And I would rename this new function to mod().

For Loop In Verilog Does Not Converge

I'm attempting to using a for loop to count the repeated leading bit in a 32-bit number. For this, I am doing:
input[31:0] A;
output reg result;
Integer i;
for (i = 31; i > -1; i = i - 1) begin
if (A[i] == 0) begin
result = result + 1;
end
else if (A[i] == 1) begin
i = -1;
end
end
However, when I synthesize the program, I receive a warning saying that the program does not converge. Am I using the for loop wrong? Before this I used i >= 0 and even used a while instead but it doesn't change the outcome. I would appreciate any help. Should I set result to 0 before running the loop?
A[i] == 1 makes number of iterations non-deterministic and causes synthesis to fail. The way around it is letting the loop to unroll till the end and use a conditional variable to handle your calculations. Something like the following:
input[31:0] A;
output reg result;
Integer i;
reg flag;
flag = 0;
for (i = 31; i > -1; i = i - 1) begin
if (flag == 0 && A[i] == 0) begin
result = result + 1;
end
else if (A[i] == 1) begin
flag = 1;
end
end
I assume that it was some type of a flop logic, since in any case this would produce state elements. So, you need to use correct nbas for result and flag.
For synthesis for loops must converge during COMPILE time. Your condition of A[i]==1 can not be determined at compile time so the loops goes from 32 to 2^31-1 before it ends.
Verilog is an HDL, which in many aspects is totally different from standard computer languages.

How to perform a sort + indexing whilst removing duplicates at the same time?

I use the following code to sort both items and an index:
There are about 256*1024*1024 entries in the values and index arrays.
The uncompressed values array occupies about 10GB, which is why I want to compress it by grouping together duplicate values of which there are many.
I've come up with the following proof of concept code, but there is a problem in the Compress method. Every time it finds a duplicate it performs a search in the index which costs O(N2) time.
I need to keep an index, because I need to be able to access elements in the array as if no de-duplication took place.
This is done, using a simple default property array property, thus mimicking the original array:
function TLookupTable.GetItems(Index: integer): TSlice;
begin
Result:= FData[FIndex[Index]];
end;
The proof of concept code (which is dog slow) is as follows.
TMyArray = class
class procedure QuickSort<T,Idx>(var Values: array of T; var Index: array of Idx; const Comparer: IComparer<T>;
L, R: Integer);
class procedure Compress<T>(const Values: array of T; var Index: array of Integer; out CompressedValues: TArray<T>; const Comparer: IComparer<T>);
end;
class procedure TMyArray.QuickSort<T,Idx>(var Values: array of T; var Index: array of Idx; const Comparer: IComparer<T>;
L, R: Integer);
var
I, J: Integer;
pivot, temp: T;
TempIdx: Idx;
begin
if (Length(Values) = 0) or ((R - L) <= 0) then
Exit;
repeat
I := L;
J := R;
pivot := Values[L + (R - L) shr 1];
repeat
while Comparer.Compare(Values[I], pivot) < 0 do Inc(I);
while Comparer.Compare(Values[J], pivot) > 0 do Dec(J);
if I <= J then begin
if I <> J then begin
temp := Values[I];
Values[I] := Values[J];
Values[J] := temp;
//Keep the index in sync
tempIdx := Index[I];
Index[I] := Index[J];
Index[J] := tempIdx;
end;
Inc(I);
Dec(J);
end;
until I > J;
if L < J then QuickSort<T,Idx>(Values, Index, Comparer, L, J);
L := I;
until I >= R;
end;
class procedure TMyArray.Compress<T>(const Values: array of T; var Index: array of integer; out CompressedValues: TArray<T>; const Comparer: IComparer<T>);
var
i,j: integer;
Count: integer;
Duplicate: integer;
begin
Count:= 256*1024*1024;
SetLength(CompressedValues, Count);
CompressedValues[0]:= Values[0];
Duplicate:= 0;
for i := 1 to High(Values) do begin
//Compress duplicate values
if Comparer.Compare(Values[i], CompressedValues[Duplicate]) = 0 then begin
//Search for the indexed item
//Very time consuming: O(N*N)
for j:= i to High(Index) do if Index[j] = i then begin
Index[j]:= Duplicate; //Fix up the index
Break;
end; {for j}
end else begin
Inc(Duplicate);
if Duplicate >= Count then begin
Inc(Count, 1024*1024);
SetLength(CompressedValues, Count);
end;
CompressedValues[Duplicate]:= Values[i];
end;
end; {for i}
SetLength(CompressedValues, Duplicate+1)
end;
How can I speed up the compress step so that it takes O(N) time?
If there is a way to both keep the index and remove duplicates and sort, all at the same time, that would be great. My answer below splits the sort and the dedup into two separate stages.
The trick is to leave the original data array alone and just sort the index.
We can then exploit the fact that the original data is in the correct order and use that to build a new index.
In addition that means that we no longer need a custom Sort function; it also moves a lot less data around.
Create the index like so:
FIndex: TArray<integer>;
....
SetLength(FIndex, Length(FAllData));
for i:= 0 to count-1 do begin
FIndex[i]:= i;
end;
TArray.Sort<Integer>(FIndex, TDelegatedComparer<integer>.Construct(
function(const Left, Right: Integer): Integer
begin
if FAllData[Left] > FAllData[Right] then Exit(1);
if FAllData[Left] < FAllData[Right] then Exit(-1);
Result:= 0;
end));
Change the TMyArray class like so:
TMyArray = class
class procedure Compress<T>(const Values: array of T; var Index: TArray<integer>; out CompressedValues: TArray<T>; const Comparer: IComparer<T>);
end;
class procedure TMyArray.Compress<T>(const Values: array of T; var Index: TArray<integer>; out CompressedValues: TArray<T>; const Comparer: IComparer<T>);
const
Equal = 0;
var
i,j: integer;
Count: integer;
Duplicate: integer;
IndexEntry: integer;
OutIndex: TArray<integer>;
begin
Count:= 16*1024*1024; //Start with something reasonable
SetLength(CompressedValues, Count);
SetLength(OutIndex, Length(Index));
Duplicate:= 0;
CompressedValues[0]:= Values[Index[0]];
OutIndex[Index[0]]:= 0;
for i:= 1 to High(Index) do begin
if Comparer.Compare(Values[Index[i]], CompressedValues[Duplicate]) = Equal then begin
OutIndex[Index[i]]:= Duplicate;
end else begin
Inc(Duplicate);
//Grow as needed
if (Duplicate >= Length(CompressedValues)) then SetLength(CompressedValues, Length(CompressedValues) + 1024*1024);
CompressedValues[Duplicate]:= Values[Index[i]];
OutIndex[Index[i]]:= Duplicate;
end;
end;
Index:= OutIndex;
SetLength(CompressedValues, Duplicate+1);
end;

Pascal, function returns wrong value

I seem to have a problem with a function in Pascal. The program is just an ordinary recursive binsearch, but it returns always the value of 4? Can anybody point to the mistake in the solution?
var i: integer;
const n = 10;
type tablice = array[1..n] of integer;
function Binsearch(const tab:tablice;l:integer;p:integer;x:integer):integer;
var s: integer;
begin
if l=p then
begin
if tab[l]=x then
Binsearch:=p
else
Binsearch:=-1;
end
else
begin
s:=(l+p) div 2;
if tab[s]<x then
l:=s+1
else
p:=s;
Binsearch(tab,l,p,x);
end;
end;
var A:tablice;
x:integer;
begin
for i:=1 to n do A[i]:=i;
x:=30;
writeln(Binsearch(A,1,n,x));
readln;
end.
On the other hand same code in C++ works fine:
using namespace std;
int rekursja(int tab[], int l, int p, int x){
if(l==p){
if(tab[l]==x) return l;
else return -1;
}else{
int s=(l+p)/2;
if(tab[s]<x) l=s+1;
else p=s;
rekursja(tab,l,p,x);
}
}
int main(){
int t[] = {1,2,3,4,5,6,7,8,9,11};
cout << rekursja(t,0,9,11);
}
Binsearch returns the result only exiting from the last call (result := -1 in this case). In the other cases no result is assigned, so a random value is shown.
Change the ricorsive call with
Binsearch:=Binsearch(tab,l,p,x);
In this way it returns the result to all previos call.

Generate character combinations based on arbitrary string and length - similar to permutations

This question was asked before in other languages but not delphi after searching SO.
see this question:How to Generate Permutations With Repeated Characters and this question: Generate all combinations of arbitrary alphabet up to arbitrary length and this one: How to generate combination of fix length strings using a set of characters?
so the question is not new but I am having a hard time translating any of this to delphi.
What I'm trying to do is generate combinations that does include repeats of characters such as this:
if we have a string of characters (specified by user): ABC and we want to generate length of three characters (also length specified by user) I would get:
AAA AAB AAC ABA ABB ABC ACA ACB ACC BAA BAB BAC etc...
This code seems to do this but in C++:
int N_LETTERS = 4;
char alphabet[] = {'a', 'b', 'c', 'd'};
std::vector<std::string> get_all_words(int length)
{
std::vector<int> index(length, 0);
std::vector<std::string> words;
while(true)
{
std::string word(length);
for (int i = 0; i < length; ++i)
word[i] = alphabet[index[i]];
words.push_back(word);
for (int i = length-1; ; --i)
{
if (i < 0) return words;
index[i]++;
if (index[i] == N_LETTERS)
index[i] = 0;
else
break;
}
}
}
This also seems to do this:
#include <iostream>
#include <string>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
void displayPermutation(string permutation[], int length){
int i;
for (i=0;i<length;i++){
cout<<permutation[i];
}
cout << endl;
}
void getPermutations(string operatorBank[], int operatorCount,
string permutation[],int permutationLength, int curIndex){
int i;
//stop recursion condition
if(curIndex == permutationLength){
displayPermutation(permutation,permutationLength);
}
else{
for(i = 0; i < operatorCount; i++){
permutation[curIndex] = operatorBank[i];
getPermutations(operatorBank,operatorCount,permutation,
permutationLength,curIndex+1);
}
}
}
int main ()
{
int operatorCount = 4;
int permutationLength = 3;
string operatorBank[] = {"+","-","*","/"};
string permutation[] = {"","","",""}; //empty string
int curIndex = 0;
getPermutations(operatorBank,operatorCount,permutation,
permutationLength,curIndex);
return 0;
}
closest to what I want in delphi is found here but does not allow AAA for example:
http://www.swissdelphicenter.ch/torry/showcode.php?id=1032
And no this is not homework in case you are guessing. No other motive but just learning.
EDIT3:
Removed all irrelevant code from question to make it easier for other people to read it and get to the answers below. Look under answers for 2 different methods to accomplish this: one using recursion and the other by using a counter function.
The examples you show make this considerably more complex than necessary, at least IMO.
What you're really looking at is a 3 digit, base 3 number. You can just count from 0 to 33 = 27, then convert each number to base 3 (using 'A', 'B' and 'C' as your digits instead of '0', '1' and '2').
In C++, the conversion could look something like this:
std::string cvt(int in) {
static const int base = 3;
static const int digits = 3;
std::string ret;
for (int i = 0; i<digits; i++) {
ret.push_back('A' + in % base);
in /= base;
}
return std::string(ret.rbegin(), ret.rend());
}
With the conversion in place, producing all the combinations becomes utterly trivial:
for (int i = 0; i < 27; i++)
std::cout << cvt(i) << "\t";
I believe converting that to Delphi should be barely short of purely mechanical -- assignments change from = to :=, % becomes mod, the integer division changes to div, the for loop changes to something like for i = 0 to 27 do, and so on. The most tedious (but ultimately quite simple) part will probably be dealing with the fact that in C++, char is simply a small integer type, on which you can do normal integer math. At least if memory serves, in Pascal (or a derivative like Delphi) you'll need ord to convert from a character to an ordinal, and chr to convert back from ordinal to character. So the 'A' + in % base; will end up something more like chr(ord('A') + in mod base);
Like I said though, it seems like nearly the entire translation could/should end up almost completely mechanical, with no requirement for real changes in how the basic algorithms work, or anything like that.
Not exactly following your sequence of output, but following a sequence similar to the way binary numbers add up...
0001
0010
0011
0100
...
The idea is simple: loop index values in an array indicating which character to use at the respective position to compose the output combination string. No recursion required.
NextCombination updates the index array so the next combination is defined, it returns true as long as not all combinations are formed. False when back to all 0's.
DefineCombinations accepts a string with chars to use (for example 'ABC') and a size of the combined string (eg: 3): this adds the following sequence to a memo:
AAA, AAB, AAC, ABA, ABB, ABC, ACA, ACB, ACC, BAA, BAB, BAC, BBA, BBB, BBC, BCA, BCB, BCC, CAA, CAB, CAC, CBA, CBB, CBC, CCA, CCB, CCC
Adapt as you wish.
function TForm1.NextCombination(var aIndices: array of Integer; const MaxValue: Integer): Boolean;
var Index : Integer;
begin
Result:=False;
Index:=High(aIndices);
while not(Result) and (Index >= Low(aIndices)) do
begin
if (aIndices[Index] < MaxValue) then
begin
{ inc current index }
aIndices[Index]:=aIndices[Index] + 1;
Result:=True;
end
else
begin
{ reset current index, process next }
aIndices[Index]:=0;
Dec(Index);
end;
end;
end;
procedure TForm1.DefineCombinations(const Chars: String; const Size: Integer);
var aIndices : array of Integer;
Index : Integer;
sData : String;
begin
try
SetLength(sData, Size);
SetLength(aIndices, Size);
repeat
for Index:=Low(aIndices) to High(aIndices) do
sData[Index + 1]:=Chars[aIndices[Index] + 1];
memo1.Lines.Add(sData);
until not(NextCombination(aIndices, Length(Chars) - 1));
finally
SetLength(aIndices, 0);
SetLength(sData, 0);
end;
end;
Let me know if I missed something from the original question.
Here it is done with Recursion (Credit to this post's accepted answer:How to Generate Permutations With Repeated Characters
program Combinations;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
procedure displayPermutation(const permutation : array of char; ilength: integer);
var
i: integer;
begin
for i := 0 to ilength - 1 do
begin
if i mod iLength = 0 then
writeln('')
else write(permutation[i]);
end;
end;
procedure getPermutations(const operatorBank: array of char; operatorCount: integer;
permutation: array of char; permutationLength, curIndex: integer);
var
i : integer;
begin
//stop recursion condition
if(curIndex = permutationLength)then
displayPermutation(permutation, permutationLength)
else
for i := 0 to operatorCount - 1 do
begin
permutation[curIndex] := operatorBank[i];
getPermutations(operatorBank,operatorCount,permutation,
permutationLength,curIndex+1);
end;
end;
var
operatorBank,permutation : array of char;
i, permutationLength, curIndex, operatorCount: integer;
Q, S : String;
begin
try
Q := ' ';
S := ' ';
while (Q <> '') and (S <> '') do
begin
Writeln('');
Write('P(N,R) N=? : ');
ReadLn(Q);
operatorCount := Length(Q);
setLength(operatorBank,operatorCount);
for i := 0 to operatorCount - 1 do
operatorBank[i] := Q[i+1];
Write('P(N,R) R=? : ');
ReadLn(S);
if S <> '' then permutationLength := StrToInt(S) + 1;
SetLength(permutation,operatorCount);
curIndex := 0;
Writeln('');
getPermutations(operatorBank, operatorCount, permutation,
permutationLength, curIndex );
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.

Resources