Skip to content

Commit 754a384

Browse files
Release 6.2.0 (#4)
# Release 6.2.0 ## New features - Support SEC100 camera ## Improvements - Better instance handling regarding FlowConfig - Increased timer to wait for camera bootUp to 20 seconds (was 12 before) ## Bugfix - SW trigger binding did not work for multiple instances - Did not check properly for existing APIs - Legacy bindings of ValueDisplay elements within UI did not work if deployed with VS Code AppSpace SDK - UI differs if deployed via Appstudio or VS Code AppSpace SDK - Fullscreen icon of iFrame was visible
1 parent 414e9c3 commit 754a384

16 files changed

Lines changed: 2591 additions & 769 deletions

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
# Changelog
22
All notable changes to this project will be documented in this file.
33

4+
## Release 6.2.0
5+
6+
### New features
7+
- Support SEC100 camera
8+
9+
### Improvements
10+
- Better instance handling regarding FlowConfig
11+
- Increased timer to wait for camera bootUp to 20 seconds (was 12 before)
12+
13+
### Bugfix
14+
- SW trigger binding did not work for multiple instances
15+
- Did not check properly for existing APIs
16+
- Legacy bindings of ValueDisplay elements within UI did not work if deployed with VS Code AppSpace SDK
17+
- UI differs if deployed via Appstudio or VS Code AppSpace SDK
18+
- Fullscreen icon of iFrame was visible
19+
420
## Release 6.1.1
521

622
### Bugfix

CSK_Module_MultiRemoteCamera/pages/pages/CSK_Module_MultiRemoteCamera/CSK_Module_MultiRemoteCamera.css

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,10 @@
5252
max-width: 30px;
5353
}
5454

55-
.myViewer_CSK_Module_MultiRemoteCamera > div > div {
56-
max-height: 600px;
57-
min-height: 600px;
55+
.myViewer_CSK_Module_MultiRemoteCamera {
56+
width: 98%;
57+
height: 600px;
58+
margin: auto;
5859
}
5960

6061
.myCustomSlider_CSK_Module_MultiRemoteCamera {
@@ -122,5 +123,5 @@
122123

123124
.myCustomButton_CSK_Module_MultiRemoteCamera {
124125
border-radius: 30px;
125-
padding-right: 0px;
126+
padding: 11px;
126127
}

CSK_Module_MultiRemoteCamera/pages/pages/CSK_Module_MultiRemoteCamera/CSK_Module_MultiRemoteCamera.html

Lines changed: 573 additions & 145 deletions
Large diffs are not rendered by default.

CSK_Module_MultiRemoteCamera/pages/src/converter.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ export function changeID(id) {
1616
export function changeStyle(theme) {
1717
const style: HTMLStyleElement = document.createElement('style');
1818
style.id ='blub'
19+
20+
const toggleSW = document.querySelectorAll("davinci-toggle-switch")
21+
toggleSW.forEach((userItem) => {
22+
const shadowToggle = userItem.shadowRoot
23+
const finalToggleSW = shadowToggle?.querySelector('div')
24+
finalToggleSW?.classList.add('hasIcon')
25+
});
26+
1927
if (theme == 'CSK_Style'){
2028
var headerToolbar = `.sopasjs-ui-header-toolbar-wrapper { background-color: #FFFFFF; }`
2129
var uiHeader = `.sopasjs-ui-header>.app-logo { margin-right:0px; }`

CSK_Module_MultiRemoteCamera/pages/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ document.addEventListener('sopasjs-ready', () => {
2020
page_Setup.remove();
2121

2222
setTimeout(() => {
23+
const element = document.querySelector("div.sjs-wrapper > div > div.sjs-fullscreen-toggle")
24+
if(element) {
25+
element.parentElement.removeChild(element)
26+
}
2327
document.title = 'CSK_Module_MultiRemoteCamera'
2428
}, 500);
2529
})

CSK_Module_MultiRemoteCamera/project.mf.xml

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ There is also the possibility to edit GigE Vision camera parameters (check also
2626
<item desc="Use SICK Pico/MidiCam2 specific GigE Vision parameters." name="SICK_Gen2">SICK Pico/MidiCam2</item>
2727
<item desc="Use Basler a2A1920-51gc specific GigE Vision parameters." name="Basler">Basler a2A1920-51gc</item>
2828
<item desc="Only custom GigE Vision config will be used. No camera / device specific predefined settings." name="Custom">CustomConfig</item>
29+
<item desc="Acquire images from SEC100 via HTTP / Websocket." name="SEC100">SEC100</item>
2930
</enum>
3031
<enum name="ColorType" trait="released">
3132
<desc>Camera color type.</desc>
@@ -330,6 +331,46 @@ INFO: Other modules can check via "Script.isServedAsEvent" if event of sepecific
330331
<desc>Notfiy image pool size to use for camera.</desc>
331332
<param desc="Size" multiplicity="1" name="size" type="int"/>
332333
</event>
334+
<event name="OnNewCameraType">
335+
<desc>Notify type of camera.</desc>
336+
<param desc="Type of camera." multiplicity="1" name="cameraType" type="string"/>
337+
</event>
338+
<event name="OnNewHTTPClientInstance">
339+
<desc>Notify instance of CSK_MultiHTTPClient module to use for SEC100 camera connection.</desc>
340+
<param desc="Instance" multiplicity="1" name="instance" type="int"/>
341+
</event>
342+
<event name="OnNewUsernameSEC">
343+
<desc>Notify username to login to SEC camera.</desc>
344+
<param desc="User" multiplicity="1" name="user" type="string"/>
345+
</event>
346+
<event name="OnNewStatusSECMode">
347+
<desc>Notify mode to run SEC camera.</desc>
348+
<param desc="Mode" multiplicity="1" name="mode" type="string"/>
349+
</event>
350+
<event name="OnNewWebSocketClientInstance">
351+
<desc>Notify instance of WebSocket client to use for SEC stream.</desc>
352+
<param desc="Instance" multiplicity="1" name="instance" type="int"/>
353+
</event>
354+
<event name="OnNewStatusCameraParameters">
355+
<desc>Notify selected camera type to show parameters within UI.</desc>
356+
<param desc="Camera type." multiplicity="1" name="camType" type="string"/>
357+
</event>
358+
<event name="OnNewPasswordSEC">
359+
<desc>Notify password for SEC user.</desc>
360+
<param desc="Password" multiplicity="1" name="password" type="string"/>
361+
</event>
362+
<event name="OnNewStatusSECStreamIsActive">
363+
<desc>Notify if SEC stream is currently active.</desc>
364+
<param desc="Status" multiplicity="1" name="status" type="bool"/>
365+
</event>
366+
<event name="OnNewEthernetInterfaceList">
367+
<desc>Notify list of available ethernet interfaces.</desc>
368+
<param desc="List" multiplicity="1" name="list" type="string"/>
369+
</event>
370+
<event name="OnNewHTTPClientInterface">
371+
<desc>Notfiy interface of HTTP client to use for SEC connection.</desc>
372+
<param desc="Interface" multiplicity="1" name="interface" type="string"/>
373+
</event>
333374
<function name="pageCalled">
334375
<desc>Function to register "OnResume" of the module UI (only as helper function).</desc>
335376
<return desc="Empty string (only needed to simplify binding)." multiplicity="1" name="emptyString" type="string"/>
@@ -604,7 +645,40 @@ According to the selected model it will use some predefined GigE Vision paramete
604645
</function>
605646
<function name="stopFlowConfigRelevantProvider">
606647
<desc>Function to stop FlowConfig relevant providers.</desc>
607-
<param desc="Instance of camera to stop." multiplicity="1" name="instance" type="int"/>
648+
</function>
649+
<function name="setSEC100HTTPClientInstance">
650+
<desc>Function to set instance of CSK_MultiHTTPClient module to use for SEC100 camera connection.</desc>
651+
<param desc="Instance" multiplicity="1" name="instance" type="int"/>
652+
</function>
653+
<function name="setSEC100Username">
654+
<desc>Function to set username to login to SEC camera.</desc>
655+
<param desc="User" multiplicity="1" name="user" type="string"/>
656+
</function>
657+
<function name="setSEC100UserPassword">
658+
<desc>Function to set password of user to login to SEC camera.</desc>
659+
<param desc="Password" multiplicity="1" name="password" type="string"/>
660+
</function>
661+
<function name="setSECMode">
662+
<desc>Function to set acquisition mode of SEC camera.</desc>
663+
<param desc="Mode" multiplicity="1" name="mode" type="string"/>
664+
</function>
665+
<function name="setWebSocketClientInstance">
666+
<desc>Function to set instance of WebSocket client module to use for SEC stream.</desc>
667+
<param desc="Instance" multiplicity="1" name="instance" type="int"/>
668+
</function>
669+
<function name="setSECStreamStatus">
670+
<desc>Function to set status of SEC camera stream.</desc>
671+
<param desc="Status" multiplicity="1" name="status" type="bool"/>
672+
</function>
673+
<function name="startSECStream">
674+
<desc>Function to start SEC stream.</desc>
675+
</function>
676+
<function name="stopSECStream">
677+
<desc>Function to stop SEC stream.</desc>
678+
</function>
679+
<function name="setSEC100HTTPClientInterface">
680+
<desc>Function to set interface of HTTP client to use for SEC100 connection.</desc>
681+
<param desc="Interface" multiplicity="1" name="interface" type="string"/>
608682
</function>
609683
</serves>
610684
</crown>
@@ -640,7 +714,7 @@ According to the selected model it will use some predefined GigE Vision paramete
640714
</crown>
641715
</crown>
642716
<meta key="author">SICK AG</meta>
643-
<meta key="version">6.1.1</meta>
717+
<meta key="version">6.2.0</meta>
644718
<meta key="priority">low</meta>
645719
<meta key="copy-protected">false</meta>
646720
<meta key="read-protected">false</meta>

CSK_Module_MultiRemoteCamera/scripts/CSK_Module_MultiRemoteCamera.lua

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,11 @@ local multiRemoteCameras_Instances = {} -- Handle all instances
6666
local multiRemoteCameraController = require('Sensors/MultiRemoteCamera/MultiRemoteCamera_Controller')
6767

6868
-- Check if specific APIs are available on device
69-
if _G.availableAPIs.default and availableAPIs.imageProvider then
69+
if _G.availableAPIs.default and _G.availableAPIs.imageProvider then
7070
_G.logger:info("I2D Support = " .. tostring(_G.availableAPIs.I2D) .. ", GigEVision support = " .. tostring(_G.availableAPIs.GigEVision))
71-
local setInstanceHandle = require('Sensors/MultiRemoteCamera/FlowConfig/MultiRemoteCamera_FlowConfig')
71+
require('Sensors/MultiRemoteCamera/FlowConfig/MultiRemoteCamera_FlowConfig')
7272
table.insert(multiRemoteCameras_Instances, multiRemoteCamera_Model.create(1)) -- create(cameraNo:int)
7373
multiRemoteCameraController.setMultiRemoteCamera_Instances_Handle(multiRemoteCameras_Instances) -- share handle of instances
74-
setInstanceHandle(multiRemoteCameras_Instances)
7574
else
7675
_G.logger:warning("CSK_MultiRemoteCamera : Relevant CROWN(s) not available on device. Module is not supported...")
7776
end

CSK_Module_MultiRemoteCamera/scripts/CSK_MultiRemoteCamera_ImageProcessing.lua

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ local nameOfModule = 'CSK_MultiRemoteCamera'
66
-- If App property "LuaLoadAllEngineAPI" is FALSE, use this to load and check for required APIs
77
-- This can improve performance of garbage collection
88
local availableAPIs = require('Sensors/MultiRemoteCamera/helper/checkAPIs') -- can be used to adjust function scope of the module related on available APIs of the device
9+
local json = require('Sensors/MultiRemoteCamera/helper/Json')
910
-----------------------------------------------------------
1011
-- Logger
1112
_G.logger = Log.SharedLogger.create('ModuleLogger')
@@ -18,6 +19,11 @@ local viewerId = scriptParams:get('viewerId') -- Viewer ID
1819
local lastImage = nil -- holds image to post process (e.g. after changing parameters)
1920
local lastTimestamp = DateTime.getTimestamp() -- timestamp to calculate processing time
2021
local fpsCounter = 0 -- counter to calculate FPS
22+
local secIP = '192.168.136.100' -- IP of SEC100 camera
23+
24+
local queue = Script.Queue.create() -- Queue to stop SEC100 streaming processing if increasing too much
25+
queue:setPriority("MID")
26+
queue:setMaxQueueSize(1)
2127

2228
-- See parameter documenation within Model
2329
local imageProcessingParams = {}
@@ -31,12 +37,18 @@ imageProcessingParams.saveAllImages = scriptParams:get('saveAllImages')
3137
imageProcessingParams.imageSaveFormat = scriptParams:get('imageSaveFormat')
3238
imageProcessingParams.imageSaveJpgFormatCompression = scriptParams:get('imageSaveJpgFormatCompression')
3339
imageProcessingParams.imageSavePngFormatCompression = scriptParams:get('imageSavePngFormatCompression')
40+
imageProcessingParams.httpClientInstance = scriptParams:get('httpClientInstance')
41+
imageProcessingParams.secUser = scriptParams:get('secUser')
42+
imageProcessingParams.secUserPassword = scriptParams:get('secUserPassword')
43+
imageProcessingParams.secMode = scriptParams:get('secMode')
44+
imageProcessingParams.secWebSocketClientInstance = scriptParams:get('secWebSocketClientInstance')
3445

3546
imageProcessingParams.activeInUI = false -- Is this instance currently selected in UI
3647
imageProcessingParams.viewerActive = false -- Should the image be shown in viewer
3748

3849
local viewer = View.create(viewerId) -- Viewer to show image
3950
local imageQueue = Script.Queue.create() -- Queue to stop processing if increasing too much
51+
local jpeg = Image.Format.JPEG.create() -- Image decoder for SEC100 images
4052

4153
-- Event to forward image to other modules
4254
Script.serveEvent("CSK_MultiRemoteCamera.OnNewImageCamera" .. cameraNumberString, "MultiRemoteCamera_OnNewImageCamera" .. cameraNumberString, 'object:1:Image, int:?')
@@ -190,6 +202,101 @@ local function deregisterCamera(camera)
190202
end
191203
Script.register("CSK_MultiRemoteCamera.OnDeregisterCamera" .. cameraNumberString, deregisterCamera)
192204

205+
--#############################################
206+
--################### SEC100 ##################
207+
--#############################################
208+
209+
local function buildDigest(_table)
210+
local hash = Hash.SHA256.create()
211+
hash:update(table.concat(_table, ":") )
212+
return hash:getHashValueHex()
213+
end
214+
215+
local function computeResponseHash(challenge, action, user, password)
216+
local nonce = challenge.nonce
217+
local ha1, ha2
218+
if challenge.salt == nil then
219+
ha1 = buildDigest({user, challenge.realm, password})
220+
else
221+
local saltStr = ""
222+
for _, i in ipairs(challenge.salt) do
223+
saltStr = saltStr .. string.char(i)
224+
end
225+
ha1 = buildDigest({user, challenge.realm, password, saltStr})
226+
end
227+
ha2 = buildDigest({"POST", action})
228+
return buildDigest({ha1, nonce, ha2})
229+
end
230+
231+
--- Function to trigger SEC camera
232+
---@param command string Command to send to SEC camera
233+
---@param data string Data for command
234+
---@return bool suc Success of trigger
235+
local function triggerSEC(command, data)
236+
237+
local _, challengeResponse = Script.callFunction('CSK_MultiHTTPClient.sendRequest' .. tostring(imageProcessingParams.httpClientInstance), 'POST', 'http://' .. secIP .. '/api/getChallenge', 80, nil, '{"data":{"user": "' .. imageProcessingParams.secUser .. '"}}', 'application/json')
238+
local challengeResponseContent = json.decode(challengeResponse)
239+
if challengeResponseContent.Response then
240+
241+
local response = json.decode(challengeResponseContent.Response)
242+
243+
if response.challenge then
244+
local responseHash = computeResponseHash(response.challenge, command, imageProcessingParams.secUser, imageProcessingParams.secUserPassword)
245+
local requestBody
246+
247+
if data then
248+
requestBody = '{"header":{"user":"' .. imageProcessingParams.secUser .. '","response":"' .. responseHash .. '","realm":"SICK Sensor","opaque":"' .. response.challenge.opaque .. '","nonce":"' .. response.challenge.nonce .. '"},' .. data .. '}'
249+
else
250+
requestBody = '{"header":{"user":"' .. imageProcessingParams.secUser .. '","response":"' .. responseHash .. '","realm":"SICK Sensor","opaque":"' .. response.challenge.opaque .. '","nonce":"' .. response.challenge.nonce .. '"}}'
251+
end
252+
253+
local requestResponse
254+
local responseData
255+
if command == 'latestSnapshot' then
256+
_, requestResponse = Script.callFunction('CSK_MultiHTTPClient.sendRequest' .. tostring(imageProcessingParams.httpClientInstance), 'POST', 'http://' .. secIP .. '/file/download/' .. command, 80, nil, requestBody, 'image/jpeg')
257+
responseData = json.decode(requestResponse)
258+
if responseData.StatusCode == 200 then
259+
local img = jpeg:decode(responseData.Response)
260+
handleOnNewImageProcessing(img)
261+
return true
262+
else
263+
_G.logger:warning(nameOfModule .. ": Request did not work.")
264+
return false
265+
end
266+
else
267+
_, requestResponse = Script.callFunction('CSK_MultiHTTPClient.sendRequest' .. tostring(imageProcessingParams.httpClientInstance), 'POST', 'http://' .. secIP .. '/api/' .. command, 80, nil, requestBody, 'application/json')
268+
responseData = json.decode(requestResponse)
269+
if responseData.StatusCode ~= 200 then
270+
_G.logger:warning(nameOfModule .. ": Request did not work.")
271+
return false
272+
else
273+
return true
274+
end
275+
end
276+
else
277+
_G.logger:warning(nameOfModule .. ": Request challenge did not work.")
278+
return false
279+
end
280+
else
281+
return false
282+
end
283+
end
284+
285+
--- Function to unpack image out of binary data (e.g. received by SEC100)
286+
---@param data binary Data
287+
---@param format enum Message format.
288+
local function unpackBinaryImage(data, format)
289+
if format == 'BINARY' then
290+
local img = jpeg:decode(data)
291+
handleOnNewImageProcessing(img)
292+
end
293+
end
294+
queue:setFunction(unpackBinaryImage)
295+
296+
--#############################################
297+
--################ SEC100 END #################
298+
--#############################################
299+
193300
--- Function to handle updates of processing parameters from Controller
194301
---@param cameraNo int Number of camera instance to update
195302
---@param parameter string Parameter to update
@@ -204,6 +311,37 @@ local function handleOnNewImageProcessingParameter(cameraNo, parameter, value)
204311
elseif cameraNo == cameraNumber then
205312
if parameter == 'saveLastImage' then
206313
saveImage(lastImage)
314+
elseif parameter == 'secWebSocketClientInstance' then
315+
--Script.deregister('CSK_MultiWebSocketClient.OnNewData' .. tostring(imageProcessingParams.secWebSocketClientInstance), unpackBinaryImage)
316+
imageProcessingParams.secWebSocketClientInstance = value
317+
elseif parameter == 'SEC100_IP' then
318+
secIP = value
319+
elseif parameter == 'secMode' then
320+
if value == 'Snapshot' then
321+
local suc = triggerSEC('SnapshotMode', '"data":{"SnapshotMode": 1}')
322+
if not suc then
323+
CSK_MultiRemoteCamera.disconnectCamera()
324+
end
325+
elseif value == 'Stream' then
326+
triggerSEC('SnapshotMode', '"data":{"SnapshotMode": 0}')
327+
local suc = triggerSEC('EventTriggerEvent')
328+
if not suc then
329+
CSK_MultiRemoteCamera.disconnectCamera()
330+
end
331+
Script.register('CSK_MultiWebSocketClient.OnNewData' .. tostring(imageProcessingParams.secWebSocketClientInstance), unpackBinaryImage)
332+
queue:setFunction(unpackBinaryImage)
333+
end
334+
elseif parameter == 'secStream' then
335+
if value == false then
336+
Script.deregister('CSK_MultiWebSocketClient.OnNewData' .. tostring(imageProcessingParams.secWebSocketClientInstance), unpackBinaryImage)
337+
else
338+
Script.register('CSK_MultiWebSocketClient.OnNewData' .. tostring(imageProcessingParams.secWebSocketClientInstance), unpackBinaryImage)
339+
queue:setFunction(unpackBinaryImage)
340+
end
341+
elseif parameter == 'SEC100_Trigger' then
342+
triggerSEC('SnapshotTriggerSnapshot')
343+
Script.sleep(200)
344+
triggerSEC('latestSnapshot')
207345
else
208346
if not parameter == 'activeInUI' then
209347
_G.logger:fine(nameOfModule .. ": Update parameter '" .. parameter .. "' of cameraNo." .. tostring(cameraNo) .. " to value = " .. tostring(value))

0 commit comments

Comments
 (0)