Flow invariant is violated - event-handling

Resources:::
ViewModel -->
#ExperimentalCoroutinesApi
#HiltViewModel
class TestimonialViewModel
#Inject constructor(
private val testimonialRepo: TestimonialRepo,
private val authRepo: AuthRepo,
val networkHelper: NetworkHelper,
private val savedStateHandle: SavedStateHandle,
) : ViewModel() {
var title by mutableStateOf(UiString.Resource(R.string.testimonial_title_create))
private set
var data by mutableStateOf(TestimonialData())
private set
private var curInx = 0
private val _areInputsValid = MutableStateFlow(false)
val areInputsValid = _areInputsValid.asStateFlow()
private val _mutableState = MutableStateFlow<TestimonialGetState>(TestimonialGetState.Loading)
val state = _mutableState.asStateFlow()
private val _stateChannel = Channel<TestimonialSubmitState>()
val stateChannel = _stateChannel.receiveAsFlow()
init {
onLoad()
}
fun onLoad() {
if (networkHelper.isConnected()) {
authRepo.getUser()?.let {
loadTestimonial(userId = it.uid)
}
} else {
_mutableState.value = TestimonialGetState.NoInternet
}
}
private fun loadTestimonial(userId: String) {
viewModelScope.launch {
testimonialRepo.getTestimonial(userId).catch { ex ->
//_mutableState.value = TestimonialGetState.Error(ex)
_mutableState.value = TestimonialGetState.Empty
}.collect {
if (it.id.isNotBlank())
title = UiString.Resource(R.string.testimonial_title_edit)
data = TestimonialData(
id = it.id,
type = it.type,
title = it.title,
testimonial = it.testimonial,
role = it.user.role,
org = it.user.org
)
when (it.type) {
TestimonialType.TEXT -> {
}
TestimonialType.IMAGE -> {
data.imgMedia = it.media.toMutableStateList()
}
TestimonialType.VIDEO -> {
data.vdoMedia = it.media.toMutableStateList()
}
}
_mutableState.value = TestimonialGetState.Success(it)
}
}
}
private fun updateTestimonial(netTestimonial: Testimonial) {
viewModelScope.launch {
testimonialRepo.updateTestimonial(netTestimonial)
.catch { ex ->
_stateChannel.send(TestimonialSubmitState.Error(ex))
}.collect {
clearErrors()
_stateChannel.send(TestimonialSubmitState.Success(it))
}
}
}
fun onEvent(event: TestimonialEvent) {
when (event) {
is TestimonialEvent.OnTypeChange -> {
data = data.copy(type = event.type)
_areInputsValid.value = validateTestimonial(event.type)
}
is TestimonialEvent.OnTitleChange -> {
data = data.copy(title = event.title)
data.titleError = onValidateText(event.title, 4, 64)
_areInputsValid.value = validateTestimonial(data.type)
}
is TestimonialEvent.OnTestimonialChange -> {
data = data.copy(testimonial = event.testimonial)
data.testimonialError = onValidateText(event.testimonial, 32, 256)
_areInputsValid.value = validateTestimonial(data.type)
}
is TestimonialEvent.OnOrgChange -> {
data = data.copy(org = event.org)
data.orgError = onValidateText(event.org, 4, 32)
_areInputsValid.value = validateTestimonial(data.type)
}
is TestimonialEvent.OnRoleChange -> {
data = data.copy(role = event.role)
data.roleError = onValidateText(event.role, 2, 32)
_areInputsValid.value = validateTestimonial(data.type)
}
is TestimonialEvent.OnMediaIndex -> {
curInx = event.inx
}
is TestimonialEvent.OnImageSelect -> {
if (event.url != null) {
data.imgMedia[curInx] = data.imgMedia[curInx].copy(localUrl = event.url)
}
data.imgError = onValidateMedia(data.imgMedia)
_areInputsValid.value = validateTestimonial(data.type)
}
is TestimonialEvent.OnImageClear -> {
data.imgMedia[event.inx] = data.imgMedia[event.inx].copy(localUrl = null, url = "")
data.imgError = UiString.Resource(R.string.the_media_can_not_be_blank)
_areInputsValid.value = validateTestimonial(data.type)
}
is TestimonialEvent.OnVideoSelect -> {
if (event.url != null) {
data.vdoMedia[curInx] = data.vdoMedia[curInx].copy(localUrl = event.url)
}
data.vdoError = onValidateMedia(data.vdoMedia)
_areInputsValid.value = validateTestimonial(data.type)
}
is TestimonialEvent.OnVideoClear -> {
data.vdoMedia[event.inx] = data.vdoMedia[event.inx].copy(localUrl = null, url = "")
data.vdoError = UiString.Resource(R.string.the_media_can_not_be_blank)
_areInputsValid.value = validateTestimonial(data.type)
}
is TestimonialEvent.OnSubmit -> {
submit()
}
}
}
private fun submit() {
authRepo.getUser()?.let {
updateTestimonial(
Testimonial(
id = data.id,
type = data.type,
title = data.title.trim(),
testimonial = data.testimonial.trim(),
userId = it.uid,
user = TestimonialUser(
name = it.name!!,
role = data.role.trim(),
org = data.org.trim(),
url = it.photoUrl.toString()
),
media = when (data.type) {
TestimonialType.TEXT -> listOf()
TestimonialType.IMAGE -> data.imgMedia
TestimonialType.VIDEO -> data.vdoMedia
}
)
)
}
}
private fun clearErrors() {
data.titleError = UiString.Empty
data.testimonialError = UiString.Empty
data.roleError = UiString.Empty
data.orgError = UiString.Empty
data.imgError = UiString.Empty
data.vdoError = UiString.Empty
}
private fun onValidateText(value: String, min: Int, max: Int): UiString {
return when {
value.isBlank() -> UiString.Resource(R.string.the_field_can_not_be_blank)
value.length > max -> UiString.Resource(R.string.data_length_more, max)
value.length in 1 until min -> UiString.Resource(R.string.data_length_less, min)
else -> UiString.Empty
}
}
private fun onValidateMedia(mediaList: List<Media>): UiString {
return if (validateMedia(mediaList)) UiString.Empty
else UiString.Resource(R.string.the_media_can_not_be_blank)
}
private fun validateMedia(mediaList: List<Media>): Boolean {
return mediaList.find { it.localUrl == null && it.url.isBlank() } == null
}
private fun validateTestimonial(type: TestimonialType): Boolean {
return when (type) {
TestimonialType.TEXT -> validateData(
data.testimonial.isNotBlank() && data.testimonialError is UiString.Empty
)
TestimonialType.IMAGE -> validateData(
validateMedia(data.imgMedia) && data.imgError is UiString.Empty
)
TestimonialType.VIDEO -> validateData(
validateMedia(data.vdoMedia) && data.vdoError is UiString.Empty
)
}
}
private fun validateData(typeValidation: Boolean): Boolean {
return (typeValidation && data.title.isNotBlank() && data.titleError is UiString.Empty
&& data.org.isNotBlank() && data.orgError is UiString.Empty
&& data.role.isNotBlank() && data.roleError is UiString.Empty
)
}
}
TestimonialsSubmitState ->
sealed class TestimonialSubmitState {
object Loading : TestimonialSubmitState()
class Error(val error: Throwable) : TestimonialSubmitState()
class Success(val status: Status) : TestimonialSubmitState()
}
UI Component ->
#OptIn(ExperimentalCoroutinesApi::class)
#Composable
fun TestimonialForm(
editViewModel: TestimonialViewModel = hiltViewModel(),
paddingValues: PaddingValues,
onNavigateTstHome: () -> Unit,
) {
val focusRequester = remember { FocusRequester() }
val radioButtonList = listOf(
TestimonialType.TEXT, TestimonialType.IMAGE, TestimonialType.VIDEO
)
var showCustomDialogWithResult by remember { mutableStateOf(false) }
val selectedOption = radioButtonList.find {
it == editViewModel.data.type
} ?: radioButtonList[0]
val focusManager = LocalFocusManager.current
val scrollState = rememberScrollState()
val areInputsValid by editViewModel.areInputsValid.collectAsStateWithLifecycle()
val events = editViewModel.stateChannel.collectAsState(initial = TestimonialSubmitState.Loading)
val event = events.value
Column(
modifier = Modifier
.fillMaxWidth()
.padding(
top = paddingValues.calculateTopPadding(),
start = spacing.medium,
end = spacing.medium
)
.verticalScroll(scrollState), verticalArrangement = Arrangement.Center
) {
Spacer(modifier = Modifier.height(spacing.medium))
TestimonialsRadioButton(selectionTypeText = "Testimonial Type",
radioButtonList = radioButtonList,
selectedOption = selectedOption,
content = {
ValidatedTextField(
value = editViewModel.data.testimonial,
onValueChange = {
editViewModel.onEvent(
TestimonialEvent.OnTestimonialChange(
it
)
)
},
label = "Testimonial",
showError = editViewModel.data.testimonialError !is UiString.Empty,
errorMessage = editViewModel.data.testimonialError,
keyboardOptions = if (editViewModel.data.testimonial.length < 512) {
KeyboardOptions(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Default
)
} else {
KeyboardOptions(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Next
)
},
keyboardActions = KeyboardActions(onNext = {
focusManager.clearFocus()
}),
modifier = Modifier.height(boxSize.extraMedium)
)
},
titleTestimonial = {
ValidatedTextField(
value = editViewModel.data.title,
onValueChange = { editViewModel.onEvent(TestimonialEvent.OnTitleChange(it)) },
label = "Title",
showError = editViewModel.data.titleError !is UiString.Empty,
errorMessage = editViewModel.data.titleError,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Next
),
keyboardActions = KeyboardActions(onNext = {
focusManager.moveFocus(FocusDirection.Down)
})
)
},
onOptionSelected = {
editViewModel.onEvent(TestimonialEvent.OnTypeChange(it))
})
Spacer(modifier = Modifier.height(spacing.medium))
ValidatedTextField(
value = editViewModel.data.org,
onValueChange = { editViewModel.onEvent(TestimonialEvent.OnOrgChange(it)) },
label = "Organisation",
showError = editViewModel.data.orgError !is UiString.Empty,
errorMessage = editViewModel.data.orgError,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Next
),
keyboardActions = KeyboardActions(onNext = {
focusManager.moveFocus(FocusDirection.Down)
})
)
Spacer(modifier = Modifier.height(spacing.medium))
ValidatedTextField(
value = editViewModel.data.role,
onValueChange = { editViewModel.onEvent(TestimonialEvent.OnRoleChange(it)) },
label = "Role",
showError = editViewModel.data.roleError !is UiString.Empty,
errorMessage = editViewModel.data.roleError,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
),
modifier = Modifier.focusRequester(focusRequester = focusRequester),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() })
)
if (selectedOption == radioButtonList[1]) {
Spacer(modifier = Modifier.height(spacing.medium))
TestGetImage()
} else if (selectedOption == radioButtonList[2]) {
Spacer(modifier = Modifier.height(spacing.medium))
TestGetVideo()
}
androidx.compose.material.Button(
onClick = {
editViewModel.onEvent(TestimonialEvent.OnSubmit)
showCustomDialogWithResult = !showCustomDialogWithResult
},
modifier = Modifier
.fillMaxWidth(1f)
.padding(spacing.medium),
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Blue, contentColor = Color.White
),
enabled = areInputsValid
) {
Text(text = "Submit", style = MaterialTheme.typography.labelLarge)
}
if (showCustomDialogWithResult) {
when (event) {
is TestimonialSubmitState.Error -> {
Log.d("validate", "Error -> ${event.error.message} and ${event.error.cause}")
}
TestimonialSubmitState.Loading -> {
LoadingAnimation()
}
is TestimonialSubmitState.Success -> {
OnSuccessfulSubmit(onDismiss = {
showCustomDialogWithResult = !showCustomDialogWithResult
}, onNavigateTstHome = onNavigateTstHome, onPositiveClick = {
editViewModel.onEvent(TestimonialEvent.OnSubmit)
onNavigateTstHome()
})
}
}
}
}
}
SuccessfulSubmit Dialog ->
Here, LaunchedEffect block is just for testing purpose.
#Composable
fun OnSuccessfulSubmit(
onDismiss: () -> Unit,
onNavigateTstHome: () -> Unit,
onPositiveClick: () -> Unit,
) {
var underReview by remember {
mutableStateOf(true)
}
LaunchedEffect(key1 = Unit, block = {
delay(2000)
underReview = false
delay(3000)
onNavigateTstHome()
})
Dialog(
onDismissRequest = onDismiss, properties = DialogProperties(dismissOnClickOutside = false)
) {
SuccessfulCard(onClick = onPositiveClick, underReview = underReview)
}
}
Error --->
When I click on Submit Button --->
Log --->
Question -->
What is this error in Log... Code part related to it --->
_stateChannel, stateChannel, updateTestimonial(), submit() ---> inherited in Testimonial form ----> last 2nd Button Component and below it event calling is DONE.
Sorry for bad structuring of the question.
Thank you.
Things I tried ---> events variable in TestimonialForm, I tried to make collectAsState( initial = null )
Also tried LaunchedEffect for when case as suggested in other similar question on Stackoverflow, but got Composable invocation error.

Related

I cannot get the html doc to load the javascript to make my calculator working

I've followed this calculator video on YouTube. I have 3 files in a folder .html, .css & .js.
I open the calculator by double-clicking the HTML file which seems to load up fine in opera with the CSS changing the appearance, however the functionality of the buttons do not work at all. Now I have double-checked the code and even gone as far as cheating with the resource material to be sure it's not an error in the code. I feel that the JavaScript isn't being read/used at all when it loads up.
https://www.youtube.com/watch?v=j59qQ7YWLxw
class calculator {
constructor(perviousOperandTextElement, currentOperandTextElement) {
this.perviousOperandTextElement = perviousOperandTextElement
this.currentOperandTextElement = currentOperandTextElement
this.clear()
}
clear() {
this.currentOperand = ''
this.perviousOperand = ''
this.operation = undefined
}
delete() {
this.currentOperand = this.currentOperand.toString().slice(0, -1)
}
appendNumber(number) {
if (number === '.' && this.currentOperand.includes('.')) return
this.currentOperand = this.currentOperand.toString() + number.tostring()
}
chooseOperation(operation) {
if (this.currentOperand === '') return
if (this.perviousOperand !== '') {
this.compute()
}
this.operation = operation
this.perviousOperand = this.currentOperand
this.currentOperand = ''
}
compute() {
let computation
const prev = parseFloat(this.previousOperand)
const current = parseFloat(this.currentOperand)
if (isNaN(prev) || isNaN(current)) return
switch (this.operation) {
case'+':
computation = prev + current
break
case '-':
computation = prev - current
break
case '*':
computation = prev * current
break
case 'รท':
computation = prev / current
break
default:
return
}
this.currentOperand = computation
this.operation = undefined
this.perviousOperand = ''
}
getDisplayNumber(number) {
const stringNumber = number.toString()
const integerDigits = parseFloat(stringNumber.split('.')[0])
const decimalDigits = stringNumber.split('.')[1]
let integerDisplay
if (isNaN(integerDigits)) {
integerDisplay = ''
} else {
integerDisplay integerDigits.toLocaleString('en', { maximumFractionDigits: 0 })
}
if (decimalDigits != null) {
return `${integerDisplay}.${decimalDigits}`
} else {
return integerDisplay
}
}
updateDisplay() {
this.currentOperandTextElement.innerText =
this.getDisplayNumber(this.currentOperand)
if (this.operation != null) {
this.previousOperandTextElement.innerText =
`${this.getDisplayNumber(this.perviousOperand)} ${this.operation}`
} else {
this.previousOperandTextElement.innerText = ''
}
}
}
const numberButtons = document.querySelectorAll('[data-number]')
const operationButtons = document.querySelectorAll('[data-operation]')
const equalsButton = document.querySelector('[data-equals]')
const deleteButton = document.querySelector('[data-delete]')
const allClearButton = document.querySelector('[data-all-clear]')
const previousOperandTextElement = document.querySelector('[data-previous-operand]')
const currentOperandTextElement = document.querySelector('[data-current-operand]')
const calculator = new calculator(previousOperandTextElement, currentOperandTextElement)
numberButtons.forEach(button => {
button.addEventListener('click', () => {
calculator.appendNumber(button.innerText)
calculator.updateDisplay()
})
})
operationButtons.forEach(button => {
button.addEventListener('click', () => {
calculator.chooseOperation(button.innerText)
calculator.updateDisplay()
})
})
equalsButton.addEventListener('click', button => {
calculator.compute()
calculator.updateDisplay()
})
allClearButton.addEventListener('click', button => {
calculator.clear()
calculator.updateDisplay()
})
deleteButton.addEventListener('click', button => {
calculator.delete()
calculator.updateDisplay()
})
Your javascript code is wrong at line 67.
We can see that at line 67, your code writes
integerDisplay integerDigits.toLocaleString('en', { maximumFractionDigits: 0 })
This is clearly wrong as you are missing a =, which assigns the value. A solution to this is to replace the line with
integerDisplay = integerDigits.toLocaleString('en', { maximumFractionDigits: 0 })

How to implement find column values and replace with new value in ag grid(like find and replace)

I am new to ag-grid angular. I want to implement a functionality where i can find each column values and replace with new values in a single column.
As ag-grid does not provide a way to Find/Replace , you can use the below algorithm to do it.
foundCell = [];
foundIndex = 0;
message = '';
//Find text in ag-grid
find(findText: string) {
let found = false;
let rowNodes: any = [];
let focusedCell = this.gridApi.getFocusedCell();
if (focusedCell) {
let lastFoundObj: any;
if (this.foundCell.length > 0) {
lastFoundObj = this.foundCell[this.foundCell.length - 1];
if (!(lastFoundObj.rowIndex == focusedCell.rowIndex && lastFoundObj.field == focusedCell.column.colId)) {
this.foundCell = [];
this.foundIndex = focusedCell.rowIndex;
}
}
}
this.gridApi.forEachNode(function (node,rowIndex) {
rowNodes.push(node);
});
for(let i=this.foundIndex; i < rowColumnData.rowNodes.length ; i++) {
let node = rowColumnData.rowNodes[i];
var rowObj = node.data;
found = false;
for (var key in rowObj) {
if (rowObj[key].includes(findText) && !this.checkIfAlreadyFound(key, node.rowIndex)) {
found = true;
this.foundCell.push({ 'field': key, 'rowIndex': node.rowIndex});
break;
}
}
if (found) {
break;
}
}
if (found) {
let lastFoundCell = this.foundCell[this.foundCell.length-1];
this.gridApi.ensureIndexVisible(lastFoundCell.rowIndex, 'middle');
this.gridApi.ensureColumnVisible(lastFoundCell.field);
this.gridApi.setFocusedCell(lastFoundCell.rowIndex,lastFoundCell.field);
this.found = true;
} else {
this.foundIndex = 0;
this.foundCell = [];
this.found = false;
this.message = 'Not found';
}
}
//Replace text in ag-grid
replace(findText: string, replaceWith: string, isReplaceAll: boolean) {
if (this.found || isReplaceAll) {
let focusedCell: any;
var cell: any;
let rowNodes = [];
focusedCell = this.gridApi.getFocusedCell();
if (focusedCell) {
cell = { rowIndex: focusedCell.rowIndex, field: focusedCell.column.colId };
} else {
cell = this.foundCell[this.foundCell.length - 1];
}
this.gridApi.forEachNode(function (node,rowIndex) {
rowNodes.push(node);
});
var rowNode: any;
var newCellValue: any;
if (isReplaceAll) {
let allfoundCell = this.findAllCells(rowNodes,findText);
for (var i = 0; i < allfoundCell.length; i++) {
cell = allfoundCell[i];
rowNode = gridApi.getDisplayedRowAtIndex(cell.rowIndex);
newCellValue = this.gridApi.getValue(cell.field, rowNode).replace(findText,replaceWith);
rowNode.setDataValue(cell.field, newCellValue);
}
} else {
rowNode = this.gridApi.getDisplayedRowAtIndex(cell.rowIndex);
newCellValue = this.gridApi.getValue(cell.field, rowNode).replace(findText,replaceWith);
if (newCellValue != rowNode.data[cell.field]) {
rowNode.setDataValue(cell.field, newCellValue);
}
}
this.found = false;
}
}

opcua session was closed by client

I have written the attached OpcUaConnector class for opc-ua connection related activities.
But it is not handling session. For example:
In opc ua configuration disabled the endpoint
In kepserver configuration did runtime > reinitializing
The windows service is throwing:
Source : system.Reactive.Core
InnerException : The session was closed by client
and stopping the windows service, as this error goes unhandled.
Can some one suggest how to handle session in opc-ua?
public class OpcUaConnector
{
private static SimplerAES simplerAES = new SimplerAES();
private DataContainer dataCointainer = null;
private UaTcpSessionChannel channel;
private string opcServerName = string.Empty;
private string opcUserId = string.Empty;
private string opcPassword = string.Empty;
private static ILog LogOpcStore;
private static System.IDisposable token;
private static uint id;
public OpcConnector(ILog Log)
{
IntializeLogOpcStore(Log);
}
private static void IntializeLogOpcStore(ILog Log)
{
LogOpcStore = Log;
}
public async Task OpenOpcConnection()
{
try
{
if ((!string.IsNullOrEmpty(this.opcServerName) & (this.opcServerName != AppMain.MyAppSettings.OpcServer)) ||
(!string.IsNullOrEmpty(this.opcUserId) & (this.opcUserId != AppMain.MyAppSettings.OpcUserId)) ||
(!string.IsNullOrEmpty(this.opcPassword) & (this.opcPassword != AppMain.MyAppSettings.OpcPassword)))
{
await channel.CloseAsync();
this.opcServerName = AppMain.MyAppSettings.OpcServer;
this.opcUserId = AppMain.MyAppSettings.OpcUserId;
this.opcPassword = AppMain.MyAppSettings.OpcPassword;
}
if (channel==null || (channel != null && (channel.State == CommunicationState.Closed || channel.State == CommunicationState.Faulted)))
{
var appDescription = new ApplicationDescription()
{
ApplicationName = "MyAppName",
ApplicationUri = $"urn:{System.Net.Dns.GetHostName()}:MyAppName",
ApplicationType = ApplicationType.Client,
};
//application data won't be deleted when uninstall
var certificateStore = new DirectoryStore(
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), MyAppName", "pki"),
true, true
);
//if the Ethernet cable unplugs or the Wifi drops out,
//you have some timeouts that can keep the session open for a while.
//There is a SessionTimeout (default of 2 min).
this.channel = new UaTcpSessionChannel(
appDescription,
certificateStore,
SignInOpc,
AppMain.MyAppSettings.OpcServer,
null,
options: new UaTcpSessionChannelOptions { SessionTimeout = 120000 });
await channel.OpenAsync();
//LogOpcStore.Info(String.Format("Opc connection sucessful"));
}
this.opcServerName = AppMain.MyAppSettings.OpcServer;
this.opcUserId = AppMain.MyAppSettings.OpcUserId;
this.opcPassword = AppMain.MyAppSettings.OpcPassword;
}
catch (Exception ex)
{
ServiceException serviceException = new ServiceException(ex.HResult + " " + ex.Message, "C052");
throw serviceException;
}
}
private static async Task RecursivelyFindNode(UaTcpSessionChannel channel, NodeId nodeid)
{
BrowseRequest browseRequest = new BrowseRequest
{
NodesToBrowse = new BrowseDescription[] { new BrowseDescription { NodeId = nodeid, BrowseDirection = BrowseDirection.Forward, ReferenceTypeId = NodeId.Parse(ReferenceTypeIds.HierarchicalReferences), NodeClassMask = (uint)NodeClass.Variable | (uint)NodeClass.Object, IncludeSubtypes = true, ResultMask = (uint)BrowseResultMask.All } },
};
BrowseResponse browseResponse = await channel.BrowseAsync(browseRequest);
foreach (var rd1 in browseResponse.Results[0].References ?? new ReferenceDescription[0])
{
uint chid = AppMain.MyTagDatabase.GetClientHandleByTag(rd1.DisplayName.ToString());
if (chid > 0)
{
AppMain.MyTagDatabase.UpdateNodeByClientHandle(chid, rd1.NodeId.ToString());
}
await RecursivelyFindNode(channel, ExpandedNodeId.ToNodeId(rd1.NodeId, channel.NamespaceUris));
}
}
public async Task CreateSubscription(DataContainer dc)
{
double curReadingValue;
try
{
dataCointainer = dc;
await RecursivelyFindNode(channel, NodeId.Parse(ObjectIds.RootFolder));
if (AppMain.MyTagDatabase.GetCntTagsNotInOpcServer() == AppMain.MyTagDatabase.GetTagCount())
{
//no need to create subscription
return;
}
//subscription timeout that is the product of PublishingInterval * LifetimeCount:
var subscriptionRequest = new CreateSubscriptionRequest
{
RequestedPublishingInterval = 1000f,
RequestedMaxKeepAliveCount = 30,
RequestedLifetimeCount = 30 * 3,
PublishingEnabled = true,
};
var subscriptionResponse = await channel.CreateSubscriptionAsync(subscriptionRequest);
id = subscriptionResponse.SubscriptionId;
var itemsToCreate = new MonitoredItemCreateRequest[AppMain.MyTagDatabase.GetTagHavingNodeCount()];
int i = 0;
foreach (var item in AppMain.MyTagDatabase.GetMyTagDatabase())
{
var itemKey = item.Key;
var itemValue = item.Value;
itemsToCreate[i] = new MonitoredItemCreateRequest { ItemToMonitor = new ReadValueId { NodeId = NodeId.Parse(itemValue.NodeId), AttributeId = AttributeIds.Value }, MonitoringMode = MonitoringMode.Reporting, RequestedParameters = new MonitoringParameters { ClientHandle = itemKey, SamplingInterval = -1, QueueSize = 0, DiscardOldest = true } };
i++;
}
var itemsRequest = new CreateMonitoredItemsRequest
{
SubscriptionId = id,
ItemsToCreate = itemsToCreate,
};
var itemsResponse = await channel.CreateMonitoredItemsAsync(itemsRequest);
token = channel.Where(pr => pr.SubscriptionId == id).Subscribe(pr =>
{
// loop thru all the data change notifications
// receiving data change notifications here
var dcns = pr.NotificationMessage.NotificationData.OfType<DataChangeNotification>();
foreach (var dcn in dcns)
{
foreach (var min in dcn.MonitoredItems)
{
MyTag MyTag = new MyTag();
bool hasValue = AppMain.MyTagDatabase.GetMyTag(min.ClientHandle, out MyTag);
if (hasValue)
{
if (double.TryParse(min.Value.Value.ToString(), out curReadingValue))
{
//LogOpcStore.Info(String.Format("ClientHandle : {0} TagName : {1} SourceTimestamp : {2} ServerTimeStamp : {3} curReadingValue : {4}", min.ClientHandle, MyTag.TagName, min.Value.SourceTimestamp, min.Value.ServerTimestamp, curReadingValue));
AddDataPointToContainer(1, MyTag.TagName, min.Value.SourceTimestamp, curReadingValue);
}
}
}
}
});
}
catch (Exception ex)
{
//If the interruption lasts longer than these timeouts then the SessionChannel and Subscriptions will need to be recreated.
channel = null;
FatalServiceException fatalserviceException = new FatalServiceException(ex.Message, "C052");
throw fatalserviceException;
}
}
public async Task DeleteSubscription()
{
try
{
var request = new DeleteSubscriptionsRequest
{
SubscriptionIds = new uint[] { id }
};
await channel.DeleteSubscriptionsAsync(request);
token.Dispose();
}
catch (Exception ex)
{
ServiceException serviceException = new ServiceException(ex.Message, "C052");
throw serviceException;
}
}
private static async Task<IUserIdentity> SignInOpc(EndpointDescription endpoint)
{
IUserIdentity userIdentity = null;
if (endpoint.UserIdentityTokens.Any(p => p.TokenType == UserTokenType.Anonymous))
{
userIdentity = new AnonymousIdentity();
}
else if (endpoint.UserIdentityTokens.Any(p => p.TokenType == UserTokenType.UserName))
{
var userName = AppMain.MyAppSettings.OpcUserId;
var password = simplerAES.Decrypt(AppMain.MyAppSettings.OpcPassword);
userIdentity = new UserNameIdentity(userName, password);
}
return userIdentity;
}
private void AddDataPointToContainer(int dataType, string source, DateTime SourceTimestampUTC, double value)
{
ConditionValue conditionValue = new ConditionValue();
long timestamp = AppMain.ServerSyncTimeStore.ConvertDateTimeToTimeStampUTC(SourceTimestampUTC);
conditionValue.dataType = dataType;
conditionValue.source = source;
conditionValue.timestamp = timestamp;
conditionValue.SourceTimestampUTC = SourceTimestampUTC;
conditionValue.LocalTime = SourceTimestampUTC.ToLocalTime();
conditionValue.value = value;
//LogOpcStore.Info(String.Format("TagName : {0} SourceTimestampUTC : {1} timestamp : {2} LocalTime : {3} curReadingValue : {4}", source, SourceTimestampUTC, timestamp, SourceTimestampUTC.ToLocalTime(), value));
dataCointainer.AddDataPoint(conditionValue);
}
}
I see you are using the project https://github.com/convertersystems/opc-ua-client.
When a server closes the session and socket (as happens when you reinitialize Kepware) the client receives immediate notification that causes the client channel to fault. A faulted channel cannot be reopened, it should be aborted and a new channel should be created.
I made this standalone test, to show that you may have to catch an exception and recreate the channel and subscription. The point of this test is to subscribe to the CurrentTime node and collect 60 datachanges. The test should last a minute. If you re-init the Kepware server in the middle of the test, the code catches the exception and recreates the channel and subscription.
[TestMethod]
public async Task OpcConnectorTest()
{
var count = 0;
UaTcpSessionChannel channel = null;
while (count < 60)
{
try
{
channel = new UaTcpSessionChannel(
this.localDescription,
this.certificateStore,
new AnonymousIdentity(),
EndpointUrl,
SecurityPolicyUris.None,
loggerFactory: this.loggerFactory);
await channel.OpenAsync();
// create the keep alive subscription.
var subscriptionRequest = new CreateSubscriptionRequest
{
RequestedPublishingInterval = 1000f,
RequestedMaxKeepAliveCount = 30,
RequestedLifetimeCount = 30 * 3,
PublishingEnabled = true,
};
var subscriptionResponse = await channel.CreateSubscriptionAsync(subscriptionRequest).ConfigureAwait(false);
var id = subscriptionResponse.SubscriptionId;
var token = channel.Where(pr => pr.SubscriptionId == id).Subscribe(pr =>
{
// loop thru all the data change notifications
var dcns = pr.NotificationMessage.NotificationData.OfType<DataChangeNotification>();
foreach (var dcn in dcns)
{
foreach (var min in dcn.MonitoredItems)
{
Console.WriteLine($"sub: {pr.SubscriptionId}; handle: {min.ClientHandle}; value: {min.Value}");
count++;
}
}
});
var itemsRequest = new CreateMonitoredItemsRequest
{
SubscriptionId = id,
ItemsToCreate = new MonitoredItemCreateRequest[]
{
new MonitoredItemCreateRequest { ItemToMonitor = new ReadValueId { NodeId = NodeId.Parse("i=2258"), AttributeId = AttributeIds.Value }, MonitoringMode = MonitoringMode.Reporting, RequestedParameters = new MonitoringParameters { ClientHandle = 12345, SamplingInterval = -1, QueueSize = 0, DiscardOldest = true } }
},
};
var itemsResponse = await channel.CreateMonitoredItemsAsync(itemsRequest);
while (channel.State == CommunicationState.Opened && count < 60)
{
await Task.Delay(1000);
}
}
catch (Exception ex)
{
Console.WriteLine($"Exception: {ex.GetType()}. {ex.Message}");
}
}
if (channel != null)
{
Console.WriteLine($"Closing session '{channel.SessionId}'.");
await channel.CloseAsync();
}
}
I know this is an old post, but I stumbled upon this problem as well. For those interested:
The problem is related to the subscription(s).
When the following code is run:
token = channel.Where(pr => pr.SubscriptionId == id).Subscribe(pr =>
{
// loop thru all the data change notifications
// receiving data change notifications here
var dcns = pr.NotificationMessage.NotificationData.OfType<DataChangeNotification>();
foreach (var dcn in dcns)
{
foreach (var min in dcn.MonitoredItems)
{
MyTag MyTag = new MyTag();
bool hasValue = AppMain.MyTagDatabase.GetMyTag(min.ClientHandle, out MyTag);
if (hasValue)
{
if (double.TryParse(min.Value.Value.ToString(), out curReadingValue))
{
//LogOpcStore.Info(String.Format("ClientHandle : {0} TagName : {1} SourceTimestamp : {2} ServerTimeStamp : {3} curReadingValue : {4}", min.ClientHandle, MyTag.TagName, min.Value.SourceTimestamp, min.Value.ServerTimestamp, curReadingValue));
AddDataPointToContainer(1, MyTag.TagName, min.Value.SourceTimestamp, curReadingValue);
}
}
}
}
});
Observable.subscribe() takes multiple arguments. You should include what to do in case of an error. For example:
token = channel.Where(pr => pr.SubscriptionId == id).Subscribe(
pr => { code to run normally... },
ex => { Log.Info(ex.Message); },
() => { }
);
See http://reactivex.io/documentation/operators/subscribe.html for more information.

Refresh sticky mobile leaderboard ad slot by changing the content URL in infinite scroll

I want to refresh sticky mobile leaderboard slot when the URL changes in infinite scroll. What do you think is the best way to go? Please let me know if you have any suggestions.
class DFPAds {
constructor() {
this.slots = [];
this.onScroll = throttle(this.loopAds, 300);
this.addEvents();
this.createAdObject = this.createAdObject.bind(this);
this.createSlotForAd = this.createSlotForAd.bind(this);
this.displayAd = this.displayAd.bind(this);
}
static get() {
return DFPAds._instance;
}
static set() {
if (!DFPAds._instance) {
DFPAds._instance = new DFPAds();
return DFPAds._instance;
} else {
throw new Error("DFPAds: instance already initialized");
}
}
addEvents() {
window.addEventListener("scroll", e => this.onScroll());
}
loopAds() {
this.slots.map(slot => this.displayAd(slot));
}
createAdObject(ad) {
let id = ad.id;
let attributes = getDataSet(ad);
let sizes = JSON.parse(attributes.sizes);
let sizeMapping;
if (attributes.sizemapping) {
attributes.sizemapping.length
? (sizeMapping = JSON.parse(attributes.sizemapping))
: (sizeMapping = null);
}
attributes.id = id;
attributes.sizes = sizes;
attributes.sizemapping = sizeMapping;
return attributes;
}
createSlotForAd(adObject) {
let {
id,
adtype,
position,
slotname,
sizes,
sizemapping,
shouldlazyload,
pagenumber,
pageid
} = adObject;
googletag.cmd.push(() => {
let slot = googletag.defineSlot(slotname, sizes, id);
if(position){
slot.setTargeting("position", position);
}
if(pagenumber){
slot.setTargeting("pagenumber", pagenumber);
}
if(pageid){
slot.setTargeting("PageID", pageid)
}
if (sizemapping) {
let mapping = googletag.sizeMapping();
sizemapping.map(size => {
mapping.addSize(size[0], size[1])
});
slot.defineSizeMapping(mapping.build());
}
slot.addService(googletag.pubads());
googletag.display(id);
shouldlazyload
? this.slots.push({ slot: slot, id: id })
: googletag.pubads().refresh([slot]);
console.log("SlotTop", slot)
});
}
displayAd(slot) {
console.log("Slottwo", slot)
let item = document.getElementById(slot.id);
if (item) {
let parent = item.parentElement;
let index = this.slots.indexOf(slot);
let parentDimensions = parent.getBoundingClientRect();
if (
(parentDimensions.top - 300) < window.innerHeight &&
parentDimensions.top + 150 > 0 &&
parent.offsetParent != null
) {
googletag.cmd.push(function() {
googletag.pubads().refresh([slot.slot]);
});
this.slots.splice(index, 1);
}
}
}
setUpAdSlots(context = document) {
let ads = [].slice.call(context.getElementsByClassName("Ad-data"));
if (ads.length) {
ads.map(ad => compose(this.createSlotForAd, this.createAdObject)(ad));
}
}
}
export default DFPAds;
And this is my Infinite Scroll code:
export default class InfiniteScroll {
constructor() {
this._end = document.getElementById('InfiniteScroll-End');
this._container = document.getElementById('InfiniteScroll-Container');
if(!this._end || !this._container)
return;
this._articles = { };
this._triggeredArticles = [];
this._currentArticle = '';
this._apiUrl = '';
this._count = 1;
this._timedOut = false;
this._loading = false;
this._ended = false;
this._viewedParameter = "&alreadyViewedContentIds=";
this._articleSelector = "InfiniteScroll-Article-";
this._articleClass = "Article-Container";
this.setStartData();
this.onScroll = throttle(this.eventScroll, 200);
this.addEvents();
}
addEvents(){
setTimeout(()=>{
window.addEventListener('scroll', (e) => this.onScroll() );
}, 2000);
}
addToStore(article){
this._articles["a" + article.id] = article;
}
addToTriggeredArticles(id){
this._triggeredArticles.push(id);
}
getRequestUrl(){
return this._apiUrl + this._viewedParameter + Object.keys(this._articles).map(key => this._articles[key].id).join();
}
getLastArticle(){
return this._articles[Object.keys(this._articles)[Object.keys(this._articles).length-1]];
}
setStartData() {
let dataset = getDataSet(this._container);
if(dataset.hasOwnProperty('apiurl')){
let article = Article.get();
if(article){
this._apiUrl = dataset.apiurl;
this._currentArticle = "a" + article.id;
this.addToStore(article);
}else{
throw(new Error('Infinite Scroll: Article not initialized.'));
}
}else{
throw(new Error('Infinite Scroll: Start object missing "apiurl" property.'));
}
}
eventScroll() {
if(this.isApproachingNext()){
this.requestNextArticle();
}
if(!this.isMainArticle(this._articles[this._currentArticle].node)){
this.updateCurrentArticle();
}
}
eventRequestSuccess(data){
this._loading = false;
if(data != ''){
if(data.hasOwnProperty('Id') && data.hasOwnProperty('Html') && data.hasOwnProperty('Url')){
this.incrementCount();
let node = document.createElement('div');
node.id = this._articleSelector + data.Id;
node.innerHTML = data.Html;
node.className = this._articleClass;
this._container.appendChild(node);
this.initArticleUpNext({
img: data.Img,
title: data.ArticleTitle,
category: data.Category,
target: node.id
});
this.addToStore(
new Article({
id: data.Id,
node: node,
url: data.Url,
title: data.Title,
count: this._count,
nielsenProps: {
section: data.NeilsenSection,
sega: data.NeilsenSegmentA,
segb: data.NeilsenSegmentB,
segc: data.NeilsenSegmentC
}
})
);
}else{
this._ended = true;
throw(new Error('Infinite Scroll: Response does not have an ID, Url and HTML property'));
}
}else{
this._ended = true;
throw(new Error('Infinite Scroll: No new article was received.'));
}
}
eventRequestError(response){
this._loading = false;
this._ended = true;
throw(new Error("Infinite Scroll: New article request failed."));
}
requestNextArticle(){
if(!this._loading){
this._loading = true;
return API.requestJSON(this.getRequestUrl())
.then(
(response)=>{this.eventRequestSuccess(response)},
(response)=>{this.eventRequestError(response)}
);
}
}
triggerViewEvent(article){
if(article.count > 1 && !this.isAlreadyTriggeredArticle(article.id)){
this.addToTriggeredArticles(article.id);
Tracker.pushView(article.url, article.count);
if(isFeatureEnabled('Nielsen') && Nielsen.get()){
Nielsen.get().eventInfiniteScroll({
id: article.id,
url: article.url,
section: article.nielsenProps.section,
sega: article.nielsenProps.sega,
segb: article.nielsenProps.segb,
segc: article.nielsenProps.segc
});
NielsenV60.trackEvent(article.title);
}
}
}
updateCurrentArticle(){
Object.keys(this._articles).map( key => {
if(this._currentArticle !== key && this.isMainArticle(this._articles[key].node)){
this._currentArticle = key;
this.updateUrl(this._articles[key]);
this.triggerViewEvent(this._articles[key]);
}
});
}
updateUrl(article){
try{
if(history.replaceState){
if(window.location.pathname !== article.url){
history.replaceState('', article.title, article.url);
}
}
}catch(e){}
}
incrementCount(){
this._count = this._count + 1;
}
initArticleUpNext(data){
this.getLastArticle().initUpNext(data);
}
isApproachingNext(){
return window.pageYOffset > this._end.offsetTop - (window.innerHeight * 2) && !this._ended && this._end.offsetTop >= 100;
}
isMainArticle(node){
if(node.getBoundingClientRect){
return (node.getBoundingClientRect().top < 80 && node.getBoundingClientRect().bottom > 70);
}else{
return false;
}
}
isAlreadyTriggeredArticle(id){
return this._triggeredArticles.indexOf(id) > -1;
}
}

E4X to JSON conversion fails for duplicate xml elements

Kindly see below code I am using to convert Mirth xml to JSON.
function E4XtoJSON(xml, ignored) {
var r, children = xml.*, attributes = xml.#*, length = children.length();
if(length == 0) {
r = xml.toString();
} else if(length == 1) {
var text = xml.text().toString();
if(text) {
r = text;
}
}
if(r == undefined) {
r = {};
for each (var child in children) {
var name = child.localName();
var json = E4XtoJSON(child, ignored);
var value = r[name];
if(value) {
if(value.length) {
value.push(json);
} else {
r[name] = [value, json]
}
} else {
r[name] = json;
}
}
}
if(attributes.length()) {
var a = {}, c = 0;
for each (var attribute in attributes) {
var name = attribute.localName();
if(ignored && ignored.indexOf(name) == -1) {
a["_" + name] = attribute.toString();
c ++;
}
}
if(c) {
if(r) a._ = r;
return a;
}
}
return r;
}
My concern is
<AdditionalMessageInformationCount AdditionalMessageInformationCount="02"><AdditionalMessageInformationQualifier>01</AdditionalMessageInformationQualifier><AdditionalMessageInformation>MEMBER MUST USE MAIL ORDER.</AdditionalMessageInformation><AdditionalMessageInformationQualifier>02</AdditionalMessageInformationQualifier><AdditionalMessageInformation>PLAN LIMITATIONS EXCEEDED</AdditionalMessageInformation></AdditionalMessageInformationCount>
Here AdditionalMessageInformation elemt is used two times so function fails to create JSON.
Kindly help if anyone have converted XML in json usingg javascript code not any API
We've had success with this version:
function E4XtoJSON(xml, ignored){
var r, children = xml.*,
attributes = xml.# * ,
length = children.length();
if (length == 0)
{
r = xml.toString();
}
else if (length == 1)
{
var text = xml.text().toString();
if (text)
{
r = text;
}
}
if (r == undefined)
{
r = {};
for each(var child in children)
{
var name = child.localName();
var json = E4XtoJSON(child, ignored);
var value = r[name];
if (value)
{
if (value instanceof Array)
{
value.push(json);
}
else
{
r[name] = [value, json]
}
}
else
{
r[name] = json;
}
}
}
if (attributes.length())
{
var a = {},
c = 0;
for each(var attribute in attributes)
{
var name = attribute.localName();
if (ignored && ignored.indexOf(name) == -1)
{
a["_" + name] = attribute.toString();
c++;
}
}
if (c)
{
if (r) a._ = r;
return a;
}
}
return r;
}
With the release of Mirth Connect version 3.3.0, you can use Mirth Connect to set your channel's interior data type to JSON. This will all be done for you.