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

Switching does now work from Configuration Page #98

Merged
merged 2 commits into from
Jun 11, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

namespace Plantmonitor.Server.Features.DeviceProgramming;

public record struct AssociatePowerOutletModel(Guid DeviceId, long SwitchOnId, long SwitchOffId);
public record struct AssociatePowerOutletModel(Guid DeviceId, long? SwitchOnId, long? SwitchOffId);
public record struct OutletModel(long SwitchOnId, long SwitchOffId, string Name, int ButtonNumber, int Channel);

[ApiController]
[Route("api/[controller]")]
Expand All @@ -14,26 +15,42 @@ public class PowerOutletController(DataContext context)
public void AssociateDeviceWithPowerOutlet([FromBody] AssociatePowerOutletModel model)
{
context.DeviceSwitchAssociations.RemoveRange(context.DeviceSwitchAssociations.Where(ass => ass.DeviceId == model.DeviceId));
if (model.SwitchOnId == null || model.SwitchOffId == null)
{
context.SaveChanges();
return;
}
context.DeviceSwitchAssociations.Add(new DeviceSwitchAssociation()
{
DeviceId = model.DeviceId,
OutletOnFk = model.SwitchOnId,
OutletOffFk = model.SwitchOffId,
OutletOnFk = model.SwitchOnId.Value,
OutletOffFk = model.SwitchOffId.Value,
});
context.SaveChanges();
}

[HttpGet("outlets")]
public IEnumerable<OutletModel> GetOutlets()
{
return context.SwitchableOutletCodes
.GroupBy(c => new { c.OutletName, c.ChannelNumber, c.ChannelBaseNumber })
.Select(c => new OutletModel(c.First(x => x.TurnsOn).Id, c.First(x => !x.TurnsOn).Id,
c.First().OutletName, c.First().ChannelNumber, c.First().ChannelBaseNumber));
}

[HttpGet("getoutlet")]
public AssociatePowerOutletModel PowerOutletForDevice(string deviceId)
public AssociatePowerOutletModel? PowerOutletForDevice(string deviceId)
{
var guid = Guid.Parse(deviceId);
var result = context.DeviceSwitchAssociations.First(ass => ass.DeviceId == guid);
var result = context.DeviceSwitchAssociations.FirstOrDefault(ass => ass.DeviceId == guid);
if (result == default) return null;
return new(result.DeviceId, result.OutletOnFk, result.OutletOffFk);
}

[HttpPost("switchoutlet")]
public void SwitchOutlet([FromServices] IDeviceApiFactory apiFactory, string ip, long code)
public void SwitchOutlet([FromServices] IDeviceApiFactory apiFactory, string ip, long codeId)
{
var code = context.SwitchableOutletCodes.First(c => c.Id == codeId).Code;
apiFactory.SwitchOutletsClient(ip).SwitchoutletAsync(code);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,24 @@
import {onDestroy, onMount} from "svelte";
import {
AppConfigurationClient,
AssociatePowerOutletModel,
CameraType,
DeviceClient,
DeviceConfigurationClient,
DeviceHealthState,
HealthState,
WebSshCredentials
OutletModel,
PowerOutletClient,
WebSshCredentials,
type IAssociatePowerOutletModel
} from "../../services/GatewayAppApi";
import {Task} from "~/types/task";
import {CvInterop, ThermalImage} from "./CvInterop";
import TextInput from "../reuseableComponents/TextInput.svelte";
import PasswordInput from "../reuseableComponents/PasswordInput.svelte";
import PictureStreamer from "../deviceProgramming/PictureStreamer.svelte";
import Select from "../reuseableComponents/Select.svelte";

const videoCanvasId = "videoCanvasId";
let configurationData: {ipFrom: string; ipTo: string; userName: string; userPassword: string} = {
ipFrom: "",
ipTo: "",
Expand All @@ -25,8 +29,11 @@
};
let configurationClient: DeviceConfigurationClient;
let devices: DeviceHealthState[] = [];
let outletByDevice: {[key: string]: AssociatePowerOutletModel | null} = {};
let previewImage = new ThermalImage();
let pictureStreamer: PictureStreamer;
let existingOutlets: OutletModel[] = [];
let selectedOutlet: OutletModel | undefined;
let logData = "";

let searchingForDevices = true;
Expand All @@ -41,12 +48,15 @@
let webSshCredentials: WebSshCredentials;
onMount(async () => {
configurationClient = new DeviceConfigurationClient();
devices = await configurationClient.getDevices();
const outletClient = new PowerOutletClient();
existingOutlets = await outletClient.getOutlets();
existingOutlets.sort((a, b) => (a.name + a.channel + a.buttonNumber).localeCompare(b.name + b.channel + b.buttonNumber));
webSshCredentials = await configurationClient.getWebSshCredentials();
await getDeviceStatus();
searchingForDevices = false;
});
onDestroy(async () => {
await pictureStreamer?.stopStreaming();
onDestroy(() => {
pictureStreamer?.stopStreaming();
});
async function openConsole(ip: string | undefined): Promise<void> {
if (ip == undefined) return;
Expand Down Expand Up @@ -102,6 +112,10 @@
pictureStreamer.stopStreaming();
pictureStreamer.showPreview(device, CameraType.Vis, 1);
}
async function checkStatus(ip: string) {
const deviceClient = new DeviceConfigurationClient();
await deviceClient.recheckDevice(ip);
}
async function updateIpRange() {
const client = new AppConfigurationClient();
client.updateIpRanges(configurationData.ipFrom, configurationData.ipTo);
Expand All @@ -116,14 +130,40 @@
configurationData.userName = "";
configurationData.userPassword = "";
}
function switchPowerOutlet(code: number | undefined) {
if (code == undefined) return;
const switchDevices = devices.filter((d) => d.health.state != undefined && d.health.state & HealthState.CanSwitchOutlets);
const outletClient = new PowerOutletClient();
switchDevices.forEach(async (d) => {
outletClient.switchOutlet(d.ip, code);
});
}
async function getDeviceStatus() {
const client = new DeviceConfigurationClient();
const outletClient = new PowerOutletClient();
try {
devices = await client.getDevices();
devices
.filter((d) => d.health.deviceId != undefined)
.forEach(async (element) => {
const deviceId = element.health.deviceId!;
var outlet = await outletClient.powerOutletForDevice(deviceId);
outletByDevice[deviceId] = outlet;
});
} catch (ex) {
devices = [];
}
}
async function switchOutlet(model: OutletModel | undefined, deviceId: string) {
const outletClient = new PowerOutletClient();
const data: IAssociatePowerOutletModel = {
deviceId: deviceId,
switchOnId: model?.switchOnId,
switchOffId: model?.switchOffId
};
outletClient.associateDeviceWithPowerOutlet(new AssociatePowerOutletModel(data));
await getDeviceStatus();
}
</script>

<div class="col-md-12 row">
Expand Down Expand Up @@ -179,14 +219,36 @@
Preview IR Video
</button>
<button on:click={() => getLogData(device.ip)} class="btn btn-primary"> Show Logs </button>
{#if device.health.deviceId != undefined}
<div style="align-items: center;" class="col-form-label col-md-12 row ps-3">
Associated Outlet:
<Select
class="col-md-8"
initialSelectedItem={outletByDevice[device.health.deviceId]?.switchOnId?.toString()}
selectedItemChanged={(x) => switchOutlet(x, device.health.deviceId ?? "")}
textSelector={(x) => `${x.name} Channel ${x.channel} Button ${x.buttonNumber}`}
idSelector={(x) => x.switchOnId.toString()}
items={existingOutlets}></Select>
</div>
{/if}
{/if}
<button on:click={() => openConsole(device.ip)} class="btn btn-primary"> Open Console </button>
<button class="btn btn-primary" on:click={async () => await checkStatus(device.ip)}>Check Device</button>
</td>
</tr>
</tbody>
</table>
{/each}
<button on:click={async () => await getDeviceStatus()} class="btn btn-primary">Update</button>
<Select
selectedItemChanged={(x) => (selectedOutlet = x)}
textSelector={(x) => `${x.name} Channel: ${x.channel} Button: ${x.buttonNumber}`}
items={existingOutlets}
class="col-md-6"></Select>
{#if selectedOutlet != undefined}
<button on:click={() => switchPowerOutlet(selectedOutlet?.switchOnId)} class="btn btn-success">Power On</button>
<button on:click={() => switchPowerOutlet(selectedOutlet?.switchOffId)} class="btn btn-danger">Power Off</button>
{/if}
</div>
<div class="col-md-6">
{#if !previewImage.dataUrl?.isEmpty()}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
await client.toggleMotorEngage(selectedDeviceData.ip, shouldBeEngaged);
}
async function updateSteps() {
if (selectedDeviceData?.ip == undefined) return;
if (selectedDeviceData?.ip == undefined || selectedDeviceData.health.deviceId == undefined) return;
const client = new MovementProgrammingClient();
movementPlan.deviceId = selectedDeviceData.health.deviceId;
movementPlan.movementPlanJson = "{}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@
});
async function updatePictureSeries() {
const pictureClient = new PictureClient();
if ($selectedDevice == undefined || $selectedDevice?.health.deviceId.isEmpty()) return;
if ($selectedDevice == undefined || $selectedDevice?.health.deviceId?.isEmpty()) return;
pictureSeries = await pictureClient.getPictureSeries($selectedDevice?.health.deviceId);
pictureSeries = pictureSeries.sort((a, b) => a.folderName.localeCompare(b.folderName)).toReversed();
}
function onSeriesSelected(series: PictureSeriesData) {
if ($selectedDevice == undefined || $selectedDevice?.health.deviceId.isEmpty()) return;
if ($selectedDevice == undefined || $selectedDevice?.health.deviceId?.isEmpty()) return;
selectedSeries = series;
const streamer = new DeviceStreaming();
const connection = streamer.replayPictures($selectedDevice.health.deviceId, series.folderName);
const connection = streamer.replayPictures($selectedDevice.health.deviceId!, series.folderName);
hubConnection?.stop();
hubConnection = connection.connection;
images = [];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<script lang="ts" generics="T">
export let idSelector: (x: T) => string = (x) => JSON.stringify(x);
export let textSelector: (x: T) => string = (x) => x + "";
export let items: T[];
export let initialSelectedItem: string | undefined = undefined;
let firstRender = true;
let selectedItem: T | undefined;
let isInitialized = false;
export let selectedItemChanged: (x: T | undefined) => void;
function onChange(event: Event & {currentTarget: EventTarget & HTMLSelectElement}) {
const id = event.currentTarget.value;
selectedItem = items.find((x) => idSelector(x) == id);
selectedItemChanged(selectedItem);
}
$: {
if (firstRender) {
firstRender = false;
} else {
selectedItem = items.find((x) => idSelector(x) == initialSelectedItem);
isInitialized = true;
}
}
function isSelected(item: T) {
return selectedItem == item;
}
</script>

{#if isInitialized}
<div class={$$restProps.class || ""}>
<select class="form-select" on:change={(x) => onChange(x)}>
<option value="-1">NA</option>
{#each items as item}
<option value={idSelector(item)} selected={isSelected(item)}>{textSelector(item)}</option>
{/each}
</select>
</div>
{/if}
Loading