Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the possiblity to use boolean expression for path animations #5

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added cbpi4ui/CBPI-ConfigForTesting/config.7z
Binary file not shown.
24 changes: 21 additions & 3 deletions cbpi4ui/src/components/dashboard/DashboardContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ export const DashboardProvider = ({ children }) => {
};

const update_prop = (id, key, value) => {

const data = [...elements2];
const index = data.findIndex((e) => e.id === selected.id);
data[index].props[key] = value;
Expand All @@ -114,10 +113,28 @@ export const DashboardProvider = ({ children }) => {

};

const update_path_condition = (id, data) => {
const update_path_condition = (id, data, direction) => {
const index = pathes.findIndex((e) => e.id === id);
const temp_pathes = [...pathes];
if(direction === "left")
{
temp_pathes[index].condition.left = data;
}
if(direction === "right")
{
temp_pathes[index].condition.right = data;
}
setPathes([...temp_pathes]);
};

// New method for updating path animation condition based on boolean expression
const update_path_condition_exp = (id, direction, data) => {
const index = pathes.findIndex((e) => e.id === id);
const temp_pathes = [...pathes];
temp_pathes[index].condition = data;
if(direction === "leftExpression")
temp_pathes[index].condition.leftExpression = data;
if(direction=== "rightExpression")
temp_pathes[index].condition.rightExpression = data;

setPathes([...temp_pathes]);
};
Expand Down Expand Up @@ -206,6 +223,7 @@ export const DashboardProvider = ({ children }) => {
update_default_prop,
update_prop,
update_path_condition,
update_path_condition_exp, // New Method added for the boolean expression
update_coordinates,
setDraggable,
update_path,
Expand Down
38 changes: 32 additions & 6 deletions cbpi4ui/src/components/dashboard/DashboardLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,10 @@ const PathSettingsItem = ({ item, checked, handleToggle }) => {
<Checkbox edge="start" checked={checked.indexOf(item.id) !== -1} tabIndex={-1} color="primary" disableRipple inputProps={{ "aria-labelledby": "A" }} />
</ListItemIcon>
<ListItemText primary={item.name} />

</ListItem>
);


};

const PathSettings = () => {
Expand All @@ -176,14 +177,19 @@ const PathSettings = () => {
const currentIndex = checked.indexOf(value);
const newChecked = [...checked];

if (currentIndex === -1) {
if (currentIndex === -1)
{
newChecked.push(value);
} else {
}
else
{
newChecked.splice(currentIndex, 1);
}
setChecked(newChecked);
actions.update_path_condition(selected_id, {left: newChecked, right: checkedRight});
} else {
actions.update_path_condition(selected_id, {left: newChecked, right: checkedRight},direction);
}
else
{
const currentIndex = checkedRight.indexOf(value);
const newChecked = [...checkedRight];

Expand All @@ -193,7 +199,19 @@ const PathSettings = () => {
newChecked.splice(currentIndex, 1);
}
setCheckedRight(newChecked);
actions.update_path_condition(selected_id, {left: checked, right: newChecked});
actions.update_path_condition(selected_id, {left: checked, right: newChecked},direction);
}
};

// Handle change of the boolean expression for path animation.
const data = useModel(selected_id);
const handleChange = (e, direction) => {
if (direction === "left")
{
actions.update_path_condition_exp(selected_id, "leftExpression", e.target.value);
}
else{
actions.update_path_condition_exp(selected_id, "rightExpression", e.target.value);
}
};

Expand All @@ -205,6 +223,11 @@ const PathSettings = () => {
return "";
}

const item = state.pathes.find((e) => e.id === selected_id);


// Add a TextField for adding the booleanExpression
var helperTextExpression = "sample expression : (\"actor1\" && \"actor2\") || (\"actor2\" && \"actor3\") \n don't forget the quote";
return (
<>
<div
Expand All @@ -221,12 +244,15 @@ const PathSettings = () => {
<PathSettingsItem item={item} checked={checked} handleToggle={handleToggle} />
))}
</List>
<TextField label="Condition expression" helperText={helperTextExpression} fullWidth value={item?.condition?.leftExpression} onChange={(e) => handleChange(e, "left")} />

Flow Right
<List disableGutters={true} dense component="nav" aria-label="main mailbox folders">
{actor.map((item) => (
<PathSettingsItem item={item} checked={checkedRight} handleToggle={(id) => handleToggle(id, "right")} />
))}
</List>
<TextField label="Condition expression" helperText={helperTextExpression} fullWidth value={item?.condition?.rightExpression} onChange={(e) => handleChange(e, "right")} />
</div>
</>
);
Expand Down
111 changes: 100 additions & 11 deletions cbpi4ui/src/components/dashboard/widgets/Path.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ export const Path = ({ id, coordinates, condition = null, stroke = 10, max_x = 4
const [dragging, setDragging] = useState(false);
const [origin, setOrigin] = useState({ x: 0, y: 0 });
const [active, setActive] = useState(false);
const [flowLeft, setFlowLeft] = useState(false)
const [flowRight, setFlowRight] = useState(false)

const [flowLeft, setFlowLeft] = useState(false);
const [flowRight, setFlowRight] = useState(false) ;
const[rightExpression, SetFlowExpRight] = useState(false); // rightExpression and useState Hook for SetFlowRightExp
const[leftExpression, SetFlowExpLeft] = useState(false); // leftExpression and useState Hook for SetFlowleftExp

useEffect(() => {

Expand All @@ -21,18 +22,106 @@ export const Path = ({ id, coordinates, condition = null, stroke = 10, max_x = 4
return obj;
}, {});

// Add a cache of actor states by name, used for calculate the expression
const actor_cacheFromName = actor.reduce((obj, item) => {
obj[item.name] = item.state;
return obj;
}, {});

const p = state.pathes.find((e) => e.id === id);
if (!p.condition?.left || p.condition?.left.length === 0) {
setFlowLeft(false)
} else {
setFlowLeft(p.condition?.left.reduce((sum, next) => sum && actor_cache[next], true));
}

if (!p.condition?.right || p.condition?.right.length === 0) {
setFlowRight(false)
} else {

// Add a control to check if a boolean expression is added in path properties
// If an expression is present in the left or right direction, we don't check the checked value for calculating animation state.
if((!p.condition?.leftExpression || p.condition?.leftExpression.length === 0)
&& (!p.condition?.rightExpression || p.condition?.rightExpression.length === 0))
{
if (!p.condition?.left || p.condition?.left.length === 0)
{
setFlowLeft(false)
}
else
{
setFlowLeft(p.condition?.left.reduce((sum, next) => sum && actor_cache[next], true));
}

if (!p.condition?.right || p.condition?.right.length === 0)
{
setFlowRight(false)
}
else
{
setFlowRight(p.condition?.right.reduce((sum, next) => sum && actor_cache[next], true));
}
}

// If the leftexpression value is entered in the path property.
// TODO : variable count optimisation
if(p.condition?.leftExpression)
{
// split the expression
var actorsId = p.condition.leftExpression.split("\"");
var bStateAction = false;
var boolExpressionLeft;
boolExpressionLeft = p.condition.leftExpression

// For each part of the string, we check if this is an actor name, if so we replace the actor name by the resulting state.
for(var actorId of actorsId)
{
// Check if we process an actorId
if(typeof actor_cacheFromName[actorId] === "boolean")
{
boolExpressionLeft = boolExpressionLeft.replace('\"'+ actorId + '\"', actor_cacheFromName[actorId].toString());
}
}
// Evaluation of the expression
// TODO : in order to be safer, plane to use something else than eval (in the craftbeerpi context, i'm not sur it can cause any security problem : TO BE CONFIRMED)
try
{
bStateAction = eval(boolExpressionLeft);
console.trace("Right, Eval de : " + boolExpressionLeft + " Result = " + bStateAction.toString());
setFlowLeft(bStateAction, true);
}
catch (error)
{
console.error("Evaluation of boolean expression failure : check your boolean expression for path animation");
}
}

// If the rightExpression value is entered in the path property.
// TODO : variable count optimisation
if(p.condition?.rightExpression)
{
// split the expression
var actorsId =p.condition.rightExpression.split("\"");
var bStateAction = false;
var boolExpressionRight;
boolExpressionRight = p.condition.rightExpression

// For each part of the string, we check if this is an actor name, if so we replace the actor name by the resulting state.
for(var actorId of actorsId)
{
// Check if we process an actorId
if(typeof actor_cacheFromName[actorId] === "boolean") // Check if this is an actor ID or just an operator.
{
boolExpressionRight.replace('\"' + actorId + '\"', actor_cacheFromName[actorId].toString());
}
}
// Evaluation of the expression
// TODO : in order to be safer, plane to use something else than eval (in the craftbeerpi context, i'm not sur it can cause any security problem : TO BE CONFIRMED)
bStateAction = eval(boolExpressionRight);
if (bStateAction != undefined)
{
console.trace("Left, Eval de : " + boolExpressionLeft + " Result = " + bStateAction.toString());
setFlowRight(bStateAction, true);
}
else
{
console.trace("Evaluation of boolean expression failure : check your boolean expression for path animation");
}
}


}, [actor]);

const draggable = state.draggable;
Expand Down
7 changes: 3 additions & 4 deletions cbpi4ui/src/components/dashboard/widgets/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import QueryBuilderIcon from '@material-ui/icons/QueryBuilder';
import ShowChartIcon from '@material-ui/icons/ShowChart';
import TextFieldsIcon from '@material-ui/icons/TextFields';
import ToggleOffIcon from '@material-ui/icons/ToggleOff';
import { CBPiCalculatorIcon, CBPiControlIcon, CBPiKettle, CBPiKettle2Icon, CBPiLedIcon, CBPiLiquidIcon, CBPiSensorIcon, CBPiSVGIcon, CBPiTankIcon, CBPiThermometerIcon } from '../../util/icons/CBPiSensorIcon';
import { CBPiCalculatorIcon, CBPiControlIcon, CBPiKettle, CBPiKettle2Icon, CBPiLedIcon, CBPiLiquidIcon, CBPiPipeIcon, CBPiSensorIcon, CBPiSVGIcon, CBPiTankIcon, CBPiThermometerIcon } from '../../util/icons/CBPiSensorIcon';
import { DashboardButton } from "./Button";
import { Calculator } from "./Calculator";
import Chart from "./Chart";
Expand All @@ -12,6 +12,7 @@ import CustomSVG from "./CustomSVG";
import { KettleControl } from "./KettleControl";
import { Led } from "./Led";
import { Liquid } from './Liquid';
import {Path} from './Path';
import { SensorData } from "./SensorData";
import Steps from "./Steps";
import KettleSVG from "./svg/kettle.svg";
Expand Down Expand Up @@ -163,9 +164,7 @@ export const widget_list = [
{ name: "height", default: 40, type: "number" },
{ name: "sensor", default: "", type: "sensor" }
],
}


},

];

Expand Down