Skip to content
Merged
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
2 changes: 1 addition & 1 deletion Procfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
worker: bundle exec rake jobs:work
rabbit_workers: bin/rails r lib/rabbit_workers.rb
web: bundle exec passenger start -p $PORT -e $RAILS_ENV --max-pool-size $MAX_POOL_SIZE
web: bundle exec passenger start -p $PORT -e $RAILS_ENV --max-pool-size ${MAX_POOL_SIZE:-1} --max-requests ${MAX_REQUESTS:-1000}
# This will perform a hard refresh on all connected browsers.
release: rails r "User.refresh_everyones_ui" && rails db:migrate && (bundle exec rake hook:release_info || true)
12 changes: 8 additions & 4 deletions app/models/device.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,15 +124,19 @@ def trim_excess_telemetry
# Give the user back the amount of sensor readings they are allowed to view.
def limited_sensor_readings_list
sensor_readings
.order(created_at: :desc)
.order(created_at: :desc, id: :desc)
.limit(DEFAULT_MAX_SENSOR_READINGS)
end

def excess_sensor_readings
sensor_readings.where(id: excess_sensor_reading_ids_subquery)
end

def excess_sensor_reading_ids_subquery
sensor_readings
.where
.not(id: limited_sensor_readings_list.pluck(:id))
.where(device_id: self.id)
.order(created_at: :desc, id: :desc)
.offset(DEFAULT_MAX_SENSOR_READINGS)
.select(:id)
end

def trim_excess_sensor_readings
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ services:
build:
context: "."
dockerfile: docker_configs/api.Dockerfile
command: bash -c "rm -f tmp/pids/server.pid && bundle exec passenger start"
command: bash -c "rm -f tmp/pids/server.pid && bundle exec passenger start --max-pool-size ${MAX_POOL_SIZE:-1} --max-requests ${MAX_REQUESTS:-1000}"
ports: ["${API_PORT}:${API_PORT}"]

mqtt:
Expand Down
7 changes: 6 additions & 1 deletion example.env
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,12 @@ CODECOV_TOKEN=

# Set the max pool size for Passenger. (Only needed if using Heroku)
# FarmBot Inc uses Heroku. Self hosters do not.
MAX_POOL_SIZE=2
MAX_POOL_SIZE=1

# Set the max number of requests until restart for Passenger.
# (Only needed if using Heroku)
# FarmBot Inc uses Heroku. Self hosters do not.
MAX_REQUESTS=1000

# This is set by Heroku and used by the frontend to show the current version.
# Most self hosting users will want to delete this.
Expand Down
62 changes: 27 additions & 35 deletions frontend/css/panels/sequences.scss
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,18 @@
}
.panel-content {
.sequence-editor-content {
overflow: hidden;
overflow-x: hidden;
overflow-y: visible;

@media screen and (max-width: 767px) {
margin-left: 5px;
padding-right: 0;
}
}
.add-command-button-container {
display: block;
height: 0;
.bp6-collapse {
padding: 0;
}
.bp6-collapse-body {
padding: 0;
}
align-items: start;
justify-content: right;
grid-template-columns: 1fr auto;
}
.drag-drop-area {
display: none;
Expand Down Expand Up @@ -258,7 +254,6 @@

.sequence-editor-sections {
.bp6-collapse {
padding: 0 1rem;
.bp6-collapse-body {
padding-bottom: 1rem;
}
Expand Down Expand Up @@ -551,8 +546,11 @@
}
}

.sequence-step-components {
padding: 0 1rem;
}

.sequence-steps {
margin-right: 2.5rem;
.sequence-step {
&.hovered {
box-shadow: 0px 0px 15px #f70;
Expand All @@ -571,10 +569,20 @@
}
}

.sequence-steps > .step-dragger {
margin-right: 2.5rem;
}

.sequence-page {
.step-dragger {
margin-right: 0;
}
.add-command-button-container {
display: none;
}
}

.step-button-cluster {
overflow-y: auto;
overflow-x: hidden;
max-height: calc(100vh - 8rem);
.text-input-wrapper {
input {
padding: 0;
Expand Down Expand Up @@ -607,7 +615,6 @@
&.designer-cluster {
margin: 0;
background: $dark_gray;
margin-bottom: 1rem;
border-radius: 5px;
overflow: unset;
padding: 0.75rem;
Expand Down Expand Up @@ -1087,11 +1094,9 @@
}

.add-command-button-container {
display: none;
position: absolute;
z-index: 9;
width: 100%;
height: 0;
.bp6-collapse {
transition-duration: 0ms !important;
}
button {
margin: 0;
}
Expand All @@ -1103,6 +1108,8 @@
height: 20px;
padding: 0.25rem;
border-radius: 2rem;
margin-bottom: -.75rem;
right: -2.5rem;
box-shadow: none !important;
background: $dark_gray !important;
i {
Expand Down Expand Up @@ -1130,7 +1137,6 @@
&.last {
.bp6-collapse {
margin-top: 1rem;
margin-right: 2.5rem;
}
.add-command {
margin-top: -2rem;
Expand All @@ -1145,29 +1151,15 @@
}
}
&.only {
position: relative;
margin: auto;
text-align: center;
height: 3rem;
.add-command {
display: none;
}
.step-button-cluster {
margin: 0;
}
}

@media screen and (max-width: 767px) {
display: block;
.add-command {
display: block;
}
}
&.open {
position: relative;
.add-command {
position: absolute;
right: -2.5rem;
transform: rotate(-45deg);
i {
transform: rotate(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
SequenceBtnGroup,
SequenceShareMenu,
SequencePublishMenu,
AddCommandButton,
isSequencePublished,
AddCommandButtonProps,
} from "../sequence_editor_middle_active";
Expand Down Expand Up @@ -835,11 +836,6 @@ describe("<AddCommandButton />", () => {
stepButtonClusterSpy.mockRestore();
});

const getAddCommandButton = async () => {
return (await import(`../sequence_editor_middle_active.tsx?m=${Math.random()}`))
.AddCommandButton;
};

const fakeProps = (): AddCommandButtonProps => ({
dispatch: jest.fn(),
index: 1,
Expand All @@ -850,10 +846,9 @@ describe("<AddCommandButton />", () => {
resources: buildResourceIndex().index,
});

it("dispatches new step position", async () => {
it("dispatches new step position", () => {
location.pathname = "";
const p = fakeProps();
const AddCommandButton = await getAddCommandButton();
const wrapper = createRenderer(<AddCommandButton {...p} />);
const button = wrapper.root.findAll(node =>
typeof node.props.onClick == "function" &&
Expand All @@ -876,12 +871,13 @@ describe("<AddCommandButton />", () => {
unmountRenderer(wrapper);
});

it("closes cluster", async () => {
const AddCommandButton = await getAddCommandButton();
it("closes cluster", () => {
const { container } = render(<AddCommandButton {...fakeProps()} />);
const cluster = container.querySelector(".add-command-button-container");
const close = container.querySelector(".step-button-cluster-close");
if (cluster && close) {
expect(cluster.classList.contains("row")).toBeTruthy();
expect(cluster.classList.contains("half-gap")).toBeTruthy();
expect(cluster.classList.contains("open")).toBeTruthy();
fireEvent.click(close);
expect(cluster.classList.contains("open")).toBeFalsy();
Expand Down
4 changes: 2 additions & 2 deletions frontend/sequences/all_steps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ export class AllSteps extends React.Component<AllStepsProps, {}> {
const hovered = this.props.visualized && this.props.hoveredStep === tag
? "hovered"
: "";
return <div className="sequence-steps"
return <div className="sequence-steps grid no-gap"
key={readThatCommentAbove}>
<DropArea callback={key => this.props.onDrop(index, key)} />
{!this.props.readOnly &&
<AddCommandButton dispatch={dispatch} index={index} stepCount={1}
sequence={this.props.sequence}
farmwareData={this.props.farmwareData}
sequences={this.props.sequences}
resources={this.props.resources} />}
<DropArea callback={key => this.props.onDrop(index, key)} />
<StepDragger
dispatch={dispatch}
step={currentStep}
Expand Down
22 changes: 12 additions & 10 deletions frontend/sequences/sequence_editor_middle_active.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -849,7 +849,19 @@ export const AddCommandButton = (props: AddCommandButtonProps) => {
"add-command-button-container",
getPositionClass(),
collapsed ? "" : "open",
"row",
"half-gap",
].join(" ")}>
<Collapse isOpen={!collapsed}>
<StepButtonCluster
close={() => setCollapsed(true)}
current={props.sequence}
dispatch={dispatch}
farmwareData={props.farmwareData}
sequences={props.sequences}
resources={props.resources}
stepIndex={index} />
</Collapse>
<button
className={"add-command fb-button gray"}
title={t("add sequence step")}
Expand All @@ -862,16 +874,6 @@ export const AddCommandButton = (props: AddCommandButtonProps) => {
}}>
<i className={"fa fa-plus"} />
</button>
<Collapse isOpen={!collapsed}>
<StepButtonCluster
close={() => setCollapsed(true)}
current={props.sequence}
dispatch={dispatch}
farmwareData={props.farmwareData}
sequences={props.sequences}
resources={props.resources}
stepIndex={index} />
</Collapse>
</div>;
};

Expand Down
52 changes: 52 additions & 0 deletions spec/models/device_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,58 @@
device.send_upgrade_request
end

it "builds a DB-only subquery for excess sensor readings" do
relation = device.excess_sensor_readings

expect(relation.to_sql).to include("OFFSET #{Device::DEFAULT_MAX_SENSOR_READINGS}")
expect(relation.to_sql).to include("\"sensor_readings\".\"id\" IN")
end

it "returns limited sensor readings in reverse chronological order" do
const_reassign(Device, :DEFAULT_MAX_SENSOR_READINGS, 2) do
oldest = FactoryBot.create(:sensor_reading,
device: device,
created_at: 2.seconds.ago,
updated_at: 2.seconds.ago)
tied_time = 1.second.ago
older_tied = FactoryBot.create(:sensor_reading,
device: device,
created_at: tied_time,
updated_at: tied_time)
newer_tied = FactoryBot.create(:sensor_reading,
device: device,
created_at: tied_time,
updated_at: tied_time)

expect(device.limited_sensor_readings_list.pluck(:id))
.to eq([newer_tied.id, older_tied.id])
expect(device.limited_sensor_readings_list.pluck(:id))
.not_to include(oldest.id)
end
end

it "trims older sensor readings beyond the device limit" do
const_reassign(Device, :DEFAULT_MAX_SENSOR_READINGS, 2) do
oldest = FactoryBot.create(:sensor_reading,
device: device,
created_at: 3.seconds.ago,
updated_at: 3.seconds.ago)
middle = FactoryBot.create(:sensor_reading,
device: device,
created_at: 2.seconds.ago,
updated_at: 2.seconds.ago)
newest = FactoryBot.create(:sensor_reading,
device: device,
created_at: 1.second.ago,
updated_at: 1.second.ago)

device.trim_excess_sensor_readings

expect(device.sensor_readings.pluck(:id)).to match_array([middle.id, newest.id])
expect(SensorReading.exists?(oldest.id)).to be(false)
end
end

it "reports unknown location in feedback payload when coordinates are missing" do
expect(Faraday).to receive(:post) do |_url, payload, _headers|
text = JSON.parse(payload)["text"]
Expand Down
Loading